chiark / gitweb /
otter(1): Change TABLE-NAME to a global option argument
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Thu, 20 May 2021 11:41:27 +0000 (12:41 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Thu, 20 May 2021 11:41:27 +0000 (12:41 +0100)
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
apitest/apitest.rs
apitest/at-bundles.rs
apitest/at-otter.rs
apitest/main.rs
docs/dev.md
docs/user.rst
src/bin/otter.rs
wdriver/wdriver.rs

index 49aded72255a9dc8f05e3d4a617acc79df86a386..766fc588c9b883225028584d4d89d6274f6d9f86 100644 (file)
@@ -166,6 +166,12 @@ pub trait Substitutor {
       .map(str::to_string)
       .collect()
   }
+
+  #[throws(AE)]
+  fn gss(&self, s: &str) -> Vec<String> 
+  where Self: Sized {
+    self.ss(&format!("--game @table@ {}", s))?
+  }
 }
 
 #[derive(Clone,Debug)]
@@ -817,10 +823,10 @@ pub fn prepare_game(ds: &DirSubst, prctx: &PathResolveContext, table: &str)
     ("game_spec", prctx.resolve(&game_spec)),
   ]);
   ds.otter_prctx(prctx, &subst.ss(
-    "--account server:                                  \
+    "--account server: --game @table@                   \
      reset                                              \
      --reset-table @specs@/test.table.toml              \
-                   @table@ @game_spec@ \
+                   @game_spec@                          \
     ")?).context("reset table")?;
 
   let instance: InstanceName = table.parse()
@@ -861,10 +867,10 @@ impl DirSubst {
       ].iter());
 
       su.otter(&subst
-                  .ss("--super                          \
+                  .ss("--super --game @table@        \
                        --account server:@nick@       \
                        --fixed-token @token@         \
-                       join-game @table@")?)?;
+                       join-game")?)?;
 
       let player = mgmt_conn.has_player(
         &subst.subst("server:@nick@")?.parse()?
index cf46b9013123a09a381e7d8cdd1afdbacbceb533..650f07eca4d41248c32f9336ce2696be9226dbfe 100644 (file)
@@ -20,7 +20,7 @@ impl Ctx {
       "big-bundle","duped-example", "chess-purple-cannon", "a purple cannon",
       &mut |ctx|
     {
-      let cmd = ctx.su().ds.ss("reset @table@ modded-spec")?;
+      let cmd = ctx.su().ds.gss("reset modded-spec")?;
       ctx.reset_game(&cmd)?;
 
       let alice = ctx.connect_player(&ctx.alice)?;
index 1810d27b5ab9832809eff6723886da60fee4d67c..2b80e81bbc0b95fabf75518e998100277d6234f8 100644 (file)
@@ -11,8 +11,8 @@ impl Ctx {
   fn library_load(&mut self) {
     self.prepare_game()?;
 
-    let command = self.su().ds.ss(
-      "library-list @table@ chess-yellow-?"
+    let command = self.su().ds.gss(
+      "library-list chess-yellow-?"
     )?;
     let output: String = self.otter(&command)?.into();
     assert!( Regex::new("(?m)^wikimedia  *chess-yellow-K  *the yellow king$")?
@@ -20,8 +20,8 @@ impl Ctx {
              .is_some(),
              "got: {}", &output);
 
-    let command = self.su().ds.ss(
-      "library-add --lib wikimedia @table@ chess-blue-?"
+    let command = self.su().ds.gss(
+      "library-add --lib wikimedia chess-blue-?"
     )?;
     let added = self.some_library_add(&command)?;
     assert_eq!(added.len(), 6);
@@ -145,7 +145,7 @@ impl Ctx {
     assert_eq!(b_pieces[b_pawns[1]].pos,
                a_pieces[a_pawns[0]].pos);
 
-    let command = self.su().ds.ss("reset @table@ demo")?;
+    let command = self.su().ds.gss("reset demo")?;
     self.reset_game(&command)?;
   }
 
@@ -177,7 +177,7 @@ impl Ctx {
       let (gy, game) = games.next();
       if !(py || gy) { break }
       let command = self.su().ds.also(&[("game",&game),("perm",&perm)])
-        .ss("reset --reset-table @perm@ @table@ @game@")?;
+        .gss("reset --reset-table @perm@ @game@")?;
       self.reset_game(&command).context(perm).context(game)?;
     }
   }
index 2f6a6cccd1718d6ad4058a3c27c9549ada917aa3..08c77a3578b27894c4cd65cf53bb155b019b5ff2 100644 (file)
@@ -570,11 +570,11 @@ impl UsualCtx {
       ("itemlib", itemlib),
       ("item",    item   ),
     ]);
-    let command = ds.ss("library-add --lib @itemlib@ @table@ @item@")?;
+    let command = ds.gss("library-add --lib @itemlib@ @item@")?;
     let added = self.some_library_add(&command)?;
     assert_eq!( added.len(), 1 );
 
-    let output: String = self.otter(&ds.ss("list-pieces @table@")?)?.into();
+    let output: String = self.otter(&ds.gss("list-pieces")?)?.into();
     assert_eq!( Regex::new(
       &format!(
         r#"(?m)(?:[^\w-]|^){}[^\w-].*\W{}(?:\W|$)"#,
@@ -596,11 +596,11 @@ impl UsualCtx {
     let ds = self.su().ds.also(&[("bundle_stem", &bundle_stem)]);
     let bundle_file = ds.subst("@examples@/@bundle_stem@.zip")?;
     let ds = ds.also(&[("bundle", &bundle_file)]);
-    self.otter(&ds.ss("upload-bundle @table@ @bundle@")?)?;
-    let mut bundles = self.otter(&ds.ss("list-bundles @table@")?)?;
+    self.otter(&ds.gss("upload-bundle @bundle@")?)?;
+    let mut bundles = self.otter(&ds.gss("list-bundles")?)?;
     let bundles = String::from(&mut bundles);
     assert!(bundles.starts_with("00000.zip Loaded"));
-    self.otter(&ds.ss("download-bundle @table@ 0")?)?;
+    self.otter(&ds.gss("download-bundle 0")?)?;
     let st = Command::new("cmp").args(&[&bundle_file, "00000.zip"]).status()?;
     if ! st.success() { panic!("cmp failed {}", st) }
 
@@ -620,8 +620,8 @@ impl UsualCtx {
 
     with(self)?;
 
-    self.otter(&ds.ss("clear-game @table@")?)?;
-    self.reset_game(&ds.ss("reset @table@ demo")?)?;
+    self.otter(&ds.gss("clear-game")?)?;
+    self.reset_game(&ds.gss("reset demo")?)?;
   }
 }
 
index 3023b81d8a3117436bb8e9026b6a5dbb460f8b2c..f8112e77ba9143cd0c40f71f9fe672e1017751f2 100644 (file)
@@ -25,11 +25,11 @@ quite verbose.  So, in another shell:
 ```
     target/debug/otter                                               \
         --account server: --config server-test.toml --spec-dir=specs \
-        reset --reset-table test server::test demo
+        --game test server::test reset --reset-table demo
 
     target/debug/otter                                               \
         --account server: --config server-test.toml --spec-dir=specs \
-        join-game server::test
+        --game server::test join-game
 ```
 
 The URL printed can then be visited in a local browser.
index 7f8e298b1d4ac70656cfb6ec9e927db7ca6b5dec..ee9a3a1f2fe50c0d0f1e73f4fc31bfab9ef0c5ed 100644 (file)
@@ -8,9 +8,9 @@ To join a game, you run a command like this on the server host:
 
 ::
 
-  otter [--nick <nick>] join-game unix:ijackson::test
-                                       /^^^^^^^  ^^^^\
-                             game owner             game name
+  otter [--nick <nick>] --game unix:ijackson::test join-game
+                                   /^^^^^^^  ^^^^\
+                         game owner             game name
 
 
 This will print a URL.  You cut and paste that URL into your browser.
@@ -155,9 +155,9 @@ The most usual game-creation command looks something like this:
 
 ::
 
-  otter reset --reset-table local-users unix:ijackson::test demo
-                           /^^^^^^^^^^^ /^^^^^^^^^^^^^^^^^' '^^^\
-                           `table spec  `game name     game spec'
+  otter unix:ijackson::test reset --reset-table local-users demo
+       /~^^^^^^^^^^^^^^^^^'                    /^^^^^^^^^^^ '^^^\
+       `game name                   table spec'         game spec'
 
 Here ``local-users`` refers to the file ``local-users.table.toml`` in the
 Otter specs directory (``/volatile/Otter/specs`` on chiark).  The table
@@ -176,9 +176,9 @@ for a different game) with something like this:
 
 ::
 
-  otter reset unix:ijackson::test demo
-             /^^^^^^^^^^^^^^^^^^' '^^^\
-          game name                   game spec
+  otter unix:ijackson::test reset demo
+       /^^^^^^^^^^^^^^^^^^'       '^^^\
+       `game name                      `game spec
 
 The ``otter`` command line tool has further subcommands for
 adding/removing players, for ad-hoc addition of pieces from the
index 21d8e6e0033474ce396372000af7a29e10fe3a01..489bfc628df739649cc4364df64e8b0a7fbbc1ed 100644 (file)
@@ -68,11 +68,20 @@ struct MainOpts {
   verbose: i32,
   superuser: bool,
   spec_dir: String,
+  game: Option<String>,
 }
 
 impl MainOpts {
-  pub fn instance_name(&self, table_name: &str) -> InstanceName {
-    match table_name.strip_prefix(":") {
+  pub fn game(&self) -> &str {
+    self.game.as_ref().map(|s| s.as_str()).unwrap_or_else(||{
+      eprintln!(
+        "game (table) name not specified; pass --game option");
+      exit(EXIT_USAGE);
+    })
+  }
+
+  pub fn instance(&self) -> InstanceName {
+    match self.game().strip_prefix(":") {
       Some(rest) => {
         InstanceName {
           account: self.account.clone(),
@@ -80,17 +89,36 @@ impl MainOpts {
         }
       }
       None => {
-        table_name.parse().unwrap_or_else(|e|{
+        self.game().parse().unwrap_or_else(|e|{
           eprintln!(
-            "instance name must start with : or be valid full name: {}",
+            "game (table) name must start with : or be valid full name: {}",
             &e);
           exit(EXIT_USAGE);
         })
       }
     }
   }
+
+  #[throws(AE)]
+  fn access_account(&self) -> Conn {
+    let mut conn = connect(self)?;
+    conn.prep_access_account(self)?;
+    conn
+  }
+
+  #[throws(AE)]
+  fn access_game(&self) -> MgmtChannelForGame {
+    self.access_account()?.chan.for_game(
+      self.instance(),
+      MgmtGameUpdateMode::Online,
+    )
+  }
 }
 
+#[derive(Default,Debug)]
+struct NoArgs { }
+fn noargs(_sa: &mut NoArgs) -> ArgumentParser { ArgumentParser::new() }
+
 struct Subcommand (
   &'static str, // command
   &'static str, // desc
@@ -266,6 +294,7 @@ fn main() {
     subcommand: String,
     subargs: Vec<String>,
     spec_dir: Option<String>,
+    game: Option<String>,
   }
   let (subcommand, subargs, mo) = parse_args::<RawMainArgs,_>(
     env::args().collect(),
@@ -290,6 +319,10 @@ fn main() {
                   StoreOption,
                   "use NICK as nick for joining games (now and in the future) \
                    (default: derive from account name");
+    ap.refer(&mut rma.game).metavar("TABLE-NAME")
+      .add_option(&["--game"],
+                  StoreOption,
+                  "Select the game to operate on");
     ap.refer(&mut rma.timezone).metavar("TZ")
       .add_option(&["--timezone"],
                   StoreOption,
@@ -354,7 +387,7 @@ fn main() {
   }, &|RawMainArgs {
     account, nick, timezone,
     access, socket_path, verbose, config_filename, superuser,
-    subcommand, subargs, spec_dir, layout,
+    subcommand, subargs, spec_dir, layout, game,
   }|{
     env_logger::Builder::new()
       .filter_level(log::LevelFilter::Info)
@@ -417,6 +450,7 @@ fn main() {
       verbose,
       superuser,
       spec_dir,
+      game,
     }))
   }, Some(&|w|{
     writeln!(w, "\nSubcommands:")?;
@@ -645,21 +679,6 @@ fn read_spec_from_path<P:SpecParse>(filename: String, _: P) -> P::T
   })().with_context(|| format!("read {} {:?}", P::S::WHAT, &filename))?
 }
 
-#[throws(AE)]
-fn access_account(ma: &MainOpts) -> Conn {
-  let mut conn = connect(&ma)?;
-  conn.prep_access_account(ma)?;
-  conn
-}
-
-#[throws(AE)]
-fn access_game(ma: &MainOpts, table_name: &String) -> MgmtChannelForGame {
-  access_account(ma)?.chan.for_game(
-    ma.instance_name(table_name),
-    MgmtGameUpdateMode::Online,
-  )
-}
-
 //---------- list-games ----------
 
 mod list_games {
@@ -710,7 +729,6 @@ mod reset_game {
 
   #[derive(Default,Debug)]
   struct Args {
-    table_name: String,
     game_spec: String,
     table_file: Option<String>,
   }
@@ -721,8 +739,6 @@ mod reset_game {
     ap.refer(&mut sa.table_file).metavar("TABLE-SPEC[-TOML]")
       .add_option(&["--reset-table"],StoreOption,
                   "reset the players and access too");
-    ap.refer(&mut sa.table_name).required()
-      .add_argument("TABLE-NAME",Store,"table name");
     ap.refer(&mut sa.game_spec).required()
       .add_argument("GAME-SPEC",Store,
                     "game spec, as found in server, \
@@ -732,11 +748,8 @@ mod reset_game {
 
   fn call(_sc: &Subcommand, ma: MainOpts, args: Vec<String>) ->Result<(),AE> {
     let args = parse_args::<Args,_>(args, &subargs, &ok_id, None);
-    let instance_name = ma.instance_name(&args.table_name);
-    let mut chan = access_account(&ma)?.chan.for_game(
-      instance_name.clone(),
-      MgmtGameUpdateMode::Bulk,
-    );
+    let instance_name = ma.instance();
+    let mut chan = ma.access_game()?;
 
     let reset_insn =
       if let Some(filename) = spec_arg_is_path(&args.game_spec) {
@@ -790,7 +803,6 @@ mod set_link {
 
   #[derive(Debug,Default)]
   struct Args {
-    table_name: String,
     kind: Option<LinkKind>,
     url: Option<String>,
   }
@@ -798,8 +810,6 @@ mod set_link {
   fn subargs(sa: &mut Args) -> ArgumentParser {
     use argparse::*;
     let mut ap = ArgumentParser::new();
-    ap.refer(&mut sa.table_name).required()
-      .add_argument("TABLE-NAME",Store,"table name");
     ap.refer(&mut sa.kind)
       .add_argument("LINK-KIND",StoreOption,"link kind");
     ap.refer(&mut sa.url)
@@ -810,7 +820,7 @@ mod set_link {
   #[throws(AE)]
   fn call(_sc: &Subcommand, ma: MainOpts, args: Vec<String>) {
     let args = parse_args::<Args,_>(args, &subargs, &ok_id, None);
-    let mut chan = access_game(&ma, &args.table_name)?;
+    let mut chan = ma.access_game()?;
 
     match args.url {
       None => {
@@ -858,7 +868,6 @@ mod join_game {
   #[derive(Default,Debug)]
   struct Args {
     reset_access: bool,
-    table_name: String,
   }
 
   fn subargs(sa: &mut Args) -> ArgumentParser {
@@ -867,14 +876,12 @@ mod join_game {
     ap.refer(&mut sa.reset_access)
       .add_option(&["--reset"],StoreTrue,
                   "generate and deliver new player access token");
-    ap.refer(&mut sa.table_name).required()
-      .add_argument("TABLE-NAME",Store,"table name");
     ap
   }
 
   fn call(_sc: &Subcommand, ma: MainOpts, args: Vec<String>) ->Result<(),AE> {
     let args = parse_args::<Args,_>(args, &subargs, &ok_id, None);
-    let mut chan = access_game(&ma, &args.table_name)?;
+    let mut chan = ma.access_game()?;
 
     let mut insns = vec![];
     match chan.has_player(&ma.account)? {
@@ -946,22 +953,11 @@ mod join_game {
 mod leave_game {
   use super::*;
 
-  #[derive(Default,Debug)]
-  struct Args {
-    table_name: String,
-  }
-
-  fn subargs(sa: &mut Args) -> ArgumentParser {
-    use argparse::*;
-    let mut ap = ArgumentParser::new();
-    ap.refer(&mut sa.table_name).required()
-      .add_argument("TABLE-NAME",Store,"table name");
-    ap
-  }
+  type Args = NoArgs;
 
   fn call(_sc: &Subcommand, ma: MainOpts, args: Vec<String>) ->Result<(),AE> {
-    let args = parse_args::<Args,_>(args, &subargs, &ok_id, None);
-    let mut chan = access_game(&ma, &args.table_name)?;
+    let _args = parse_args::<Args,_>(args, &noargs, &ok_id, None);
+    let mut chan = ma.access_game()?;
 
     let player = match chan.has_player(&ma.account)? {
       None => {
@@ -988,22 +984,11 @@ mod leave_game {
 mod delete_game {
   use super::*;
 
-  #[derive(Default,Debug)]
-  struct Args {
-    table_name: String,
-  }
-
-  fn subargs(sa: &mut Args) -> ArgumentParser {
-    use argparse::*;
-    let mut ap = ArgumentParser::new();
-    ap.refer(&mut sa.table_name).required()
-      .add_argument("TABLE-NAME",Store,"table name");
-    ap
-  }
+  type Args = NoArgs;
 
   fn call(_sc: &Subcommand, ma: MainOpts, args: Vec<String>) ->Result<(),AE> {
-    let args = parse_args::<Args,_>(args, &subargs, &ok_id, None);
-    let mut chan = access_game(&ma, &args.table_name)?;
+    let _args = parse_args::<Args,_>(args, &noargs, &ok_id, None);
+    let mut chan = ma.access_game()?;
     let game = chan.game.clone();
     chan.cmd(&MC::DestroyGame { game })?;
     Ok(())
@@ -1020,7 +1005,6 @@ mod delete_game {
 
 #[derive(Debug,Default)]
 struct LibGlobArgs {
-  table_name: String,
   lib: Option<String>,
   pat: Option<String>,
 }
@@ -1031,8 +1015,6 @@ impl LibGlobArgs {
     ap: &'_ mut ArgumentParser<'ap>
   ) {
     use argparse::*;
-    ap.refer(&mut self.table_name).required()
-      .add_argument("TABLE-NAME",Store,"table name");
     ap.refer(&mut self.lib).metavar("LIBRARY")
       .add_option(&["--lib"],StoreOption,"look only in LIBRARY");
     ap.refer(&mut self.pat)
@@ -1064,7 +1046,7 @@ mod library_list {
   #[throws(AE)]
   fn call(_sc: &Subcommand, ma: MainOpts, args: Vec<String>) {
     let args = parse_args::<Args,_>(args, &subargs, &ok_id, None);
-    let mut chan = access_game(&ma, &args.table_name)?;
+    let mut chan = ma.access_game()?;
 
     if args.lib.is_none() && args.pat.is_none() {
       let game = chan.game.clone();
@@ -1128,7 +1110,7 @@ mod library_add {
     const MAGIC: &str = "mgmt-library-load-marker";
 
     let args = parse_args::<Args,_>(args, &subargs, &ok_id, None);
-    let mut chan = access_game(&ma, &args.tlg.table_name)?;
+    let mut chan = ma.access_game()?;
     let (pieces, _pcaliases) = chan.list_pieces()?;
     let markers = pieces.iter().filter(|p| p.itemname.as_str() == MAGIC)
       .collect::<Vec<_>>();
@@ -1351,23 +1333,12 @@ mod library_add {
 mod list_pieces {
   use super::*;
 
-  #[derive(Default,Debug)]
-  struct Args {
-    table_name: String,
-  }
-
-  fn subargs(sa: &mut Args) -> ArgumentParser {
-    use argparse::*;
-    let mut ap = ArgumentParser::new();
-    ap.refer(&mut sa.table_name).required()
-      .add_argument("TABLE-NAME",Store,"table name");
-    ap
-  }
+  type Args = NoArgs;
 
   #[throws(AE)]
   fn call(_sc: &Subcommand, ma: MainOpts, args: Vec<String>) {
-    let args = parse_args::<Args,_>(args, &subargs, &ok_id, None);
-    let mut chan = access_game(&ma, &args.table_name)?;
+    let _args = parse_args::<Args,_>(args, &noargs, &ok_id, None);
+    let mut chan = ma.access_game()?;
     let (pieces, pcaliases) = chan.list_pieces()?;
     for p in pieces {
       println!("{:?}", p);
@@ -1487,7 +1458,6 @@ mod alter_game_adhoc {
 
   #[derive(Default,Debug)]
   struct Args {
-    table_name: String,
     insns: Vec<String>,
   }
 
@@ -1497,8 +1467,6 @@ mod alter_game_adhoc {
   ) -> ArgumentParser<'ap> {
     use argparse::*;
     let mut ap = ArgumentParser::new();
-    ap.refer(&mut sa.table_name).required()
-      .add_argument("TABLE-NAME",Store,"table name");
     ap.refer(&mut sa.insns).required()
       .add_argument(format!("{}-INSN", ahf.metavar()).leak(),
                     Collect,
@@ -1513,7 +1481,7 @@ mod alter_game_adhoc {
 
     let subargs: ApMaker<_> = &|sa| subargs(sa,ahf);
     let args = parse_args::<Args,_>(args, subargs, &ok_id, None);
-    let mut chan = access_game(&ma, &args.table_name)?;
+    let mut chan = ma.access_game()?;
 
     let insns: Vec<MgmtGameInstruction> = ahf.parse(args.insns, "insn")?;
     let resps = chan.alter_game(insns,None)?;
@@ -1543,15 +1511,12 @@ mod upload_bundle {
 
   #[derive(Default,Debug)]
   struct Args {
-    table_name: String,
     bundle_file: String,
   }
 
   fn subargs(sa: &mut Args) -> ArgumentParser {
     use argparse::*;
     let mut ap = ArgumentParser::new();
-    ap.refer(&mut sa.table_name).required()
-      .add_argument("TABLE-NAME",Store,"table name");
     ap.refer(&mut sa.bundle_file).required()
       .add_argument("BUNDLE",Store,"bundle file");
     ap
@@ -1560,8 +1525,7 @@ mod upload_bundle {
   #[throws(AE)]
   fn call(_sc: &Subcommand, ma: MainOpts, args: Vec<String>) {
     let args = parse_args::<Args,_>(args, &subargs, &ok_id, None);
-    let instance_name = ma.instance_name(&args.table_name);
-    let mut chan = access_game(&ma, &args.table_name)?;
+    let mut chan = ma.access_game()?;
     let f = File::open(&args.bundle_file)
       .with_context(|| args.bundle_file.clone())
       .context("open bundle file")?;
@@ -1576,7 +1540,7 @@ mod upload_bundle {
     f.rewind().context("rewind bundle file")?;
     let cmd = MC::UploadBundle {
       size,
-      game: instance_name.clone(),
+      game: ma.instance(),
       hash: bundles::Hash(hash.into()), kind,
       progress: MgmtChannel::PROGRESS,
     };
@@ -1601,26 +1565,14 @@ mod upload_bundle {
 mod list_bundles {
   use super::*;
 
-  #[derive(Default,Debug)]
-  struct Args {
-    table_name: String,
-  }
-
-  fn subargs(sa: &mut Args) -> ArgumentParser {
-    use argparse::*;
-    let mut ap = ArgumentParser::new();
-    ap.refer(&mut sa.table_name).required()
-      .add_argument("TABLE-NAME",Store,"table name");
-    ap
-  }
+  type Args = NoArgs;
 
   #[throws(AE)]
   fn call(_sc: &Subcommand, ma: MainOpts, args: Vec<String>) {
-    let args = parse_args::<Args,_>(args, &subargs, &ok_id, None);
-    let instance_name = ma.instance_name(&args.table_name);
-    let mut chan = access_game(&ma, &args.table_name)?;
+    let _args = parse_args::<Args,_>(args, &noargs, &ok_id, None);
+    let mut chan = ma.access_game()?;
     let resp = chan.cmd(&MC::ListBundles {
-      game: instance_name.clone(),
+      game: ma.instance(),
     })?;
     if_let!{ MR::Bundles { bundles } = resp;
              else throw!(anyhow!("unexpected {:?}", &resp)) };
@@ -1643,7 +1595,6 @@ mod download_bundle {
 
   #[derive(Default,Debug)]
   struct Args {
-    table_name: String,
     index: bundles::Index,
     output: Option<PathBuf>,
   }
@@ -1651,8 +1602,6 @@ mod download_bundle {
   fn subargs(sa: &mut Args) -> ArgumentParser {
     use argparse::*;
     let mut ap = ArgumentParser::new();
-    ap.refer(&mut sa.table_name).required()
-      .add_argument("TABLE-NAME",Store,"table name");
     ap.refer(&mut sa.index).required()
       .add_argument("INDEX",Store,"bundle number");
     ap.refer(&mut sa.output).metavar("OUTPUT")
@@ -1664,8 +1613,7 @@ mod download_bundle {
   #[throws(AE)]
   fn call(_sc: &Subcommand, ma: MainOpts, args: Vec<String>) {
     let args = parse_args::<Args,_>(args, &subargs, &ok_id, None);
-    let instance_name = ma.instance_name(&args.table_name);
-    let mut chan = access_game(&ma, &args.table_name)?;
+    let mut chan = ma.access_game()?;
     let kind = bundles::Kind::only();
     let id = bundles::Id { kind, index: args.index };
     let path = args.output.unwrap_or_else(|| id.to_string().into());
@@ -1686,7 +1634,7 @@ mod download_bundle {
     };
     let mut f = BufWriter::new(f);
     let cmd = MC::DownloadBundle {
-      game: instance_name.clone(),
+      game: ma.instance(),
       id,
     };
     chan.cmd_withbulk(&cmd, &mut io::empty(), &mut f,
@@ -1711,28 +1659,16 @@ mod download_bundle {
 mod clear_game {
   use super::*;
 
-  #[derive(Default,Debug)]
-  struct Args {
-    table_name: String,
-  }
-
-  fn subargs(sa: &mut Args) -> ArgumentParser {
-    use argparse::*;
-    let mut ap = ArgumentParser::new();
-    ap.refer(&mut sa.table_name).required()
-      .add_argument("TABLE-NAME",Store,"table name");
-    ap
-  }
+  type Args = NoArgs;
 
   #[throws(AE)]
   fn call(_sc: &Subcommand, ma: MainOpts, args: Vec<String>) {
-    let args = parse_args::<Args,_>(args, &subargs, &ok_id, None);
-    let instance_name = ma.instance_name(&args.table_name);
-    let mut chan = access_game(&ma, &args.table_name)?;
+    let _args = parse_args::<Args,_>(args, &noargs, &ok_id, None);
+    let mut chan = ma.access_game()?;
 
     chan.alter_game(vec![MGI::ClearGame{ }], None)
       .context("clear table")?;
-    chan.cmd(&MC::ClearBundles { game: instance_name.clone() })
+    chan.cmd(&MC::ClearBundles { game: ma.instance() })
       .context("clear bundles")?;
   }
 
index 00ca2948f21bd9569220449ab9512925b3d67a7c..259e2efed061c177e54c45f0250ee46eaa979b4b 100644 (file)
@@ -624,9 +624,9 @@ impl<'g> WindowGuard<'g> {
   #[throws(AE)]
   pub fn otter(&mut self, verb: &[&str], args: &[&str]) {
     let args: Vec<String> =
-      ["--account", "server:"].iter().cloned().map(Into::into)
+      ["--account", "server:", "--game", &self.w.table()]
+      .iter().cloned().map(Into::into)
       .chain(verb.iter().cloned().map(Into::into))
-      .chain(iter::once(self.w.table()))
       .chain(args.iter().cloned().map(Into::into))
       .collect();
     self.su.ds.otter(&args)?;