How can I use clap "subcommands" based on flags?

107 Views Asked by At

I have an rsync-like rust program. The main user interface is something like program src dest.

It needs to invoke itself on another machine over ssh in order to read or write files. This is an implementation detail, and I don't want to really expose it as a subcommand, and risk src being misinterpreted.

I'd like to run these implementation details using flags.

  • program --read file would start writing the file to stdout.
  • program --write file would start writing stdin to the file.
  • program ./local user@remote:remote/file would write ./local to the stdin of ssh user@remote program --write remote/file.
  • program user@remote:remote/file ./local would write the stdout of ssh user@remote program --read file to ./local.

I've tried using the clap derive API's group feature for its ArgGroups. The derive tutorial example flattens structs.

use std::ffi::OsString;
use clap::{Args, Parser};

#[derive(Parser, Debug)]
struct RsyncExample {
    #[command(flatten)]
    inner_op: ImplOperation,

    #[command(flatten)]
    transfer: Option<FileTransfer>,
}

#[derive(Args, Clone, Debug)]
#[group(required = false, multiple = false, conflicts_with = "FileTransfer")]
struct ImplOperation {
    #[arg(long)]
    read: bool,
    #[arg(long)]
    write: bool,
}

#[derive(Args, Clone, Debug)]
struct FileTransfer {
    from: OsString,
    to: OsString,
}

fn main() {
    let cli = RsyncExample::parse();
    println!("{cli:?}");
}

While the following will rightly disallow specifying both --read --write, and disallow --read file1 file2, the error message is a bit confusing. It still specifies FROM and TO.

$ cargo run -- --read from
error: the argument '--read' cannot be used with:
  <FROM>
  <TO>

Usage: blktrans --read <FROM> <TO>

For more information, try '--help'.

And even with this, I can't figure out how to allow a single file with those flags.

I have considered linking to this program with a different name, and dispatching based on argv[0]. But I'd like to avoid that if possible.

0

There are 0 best solutions below