0

I am trying to have a CLI tool that redacts files according to some specified regexes.

In debugging, as an example:

cargo run -- folder ./tests/test_files -t emails ip

Or in production, as an example:

raf folder ./tests/test_files -t emails ip

folder is a subcommand, the first parameter is the path to the folder and the -t or --types parameter is supposed to have a list of regexes types (e.g. a regex corresponding to email, that corresponding to ip addresses and so on).

Below is a list of structs that attempted to achieve this:

use clap::{Parser, Subcommand, Args};

#[derive(Debug, Parser)]
#[clap(author, version, about, name = "raf")]
pub struct Opts {
    #[clap(subcommand)]
    pub cmd: FileOrFolder,
}

#[derive(Debug, Subcommand)]
pub enum FileOrFolder {
    #[clap(name = "folder")]
    Folder(FolderOpts),
    #[clap(name = "file")]
    File(FileOpts),
}

#[derive(Args, Debug)]
pub struct FolderOpts {
    /// `path` of the directory in which all files should be redacted, e.g. ./tests/test_files
    #[clap(parse(from_os_str))]
    pub path: std::path::PathBuf,

    /// The type of redaction to be applied to the files, e.g. -t sgNRIC emails
    #[clap(short, long)]
    pub types: Vec<String>,
}

#[derive(Args, Debug)]
pub struct FileOpts {
    #[clap(parse(from_os_str))]
    pub path: std::path::PathBuf,
    #[clap(short, long)]
    pub types: Vec<String>,
}

Basically, the field types of structs FolderOpts and FileOpts is problematic.

The runtime error is:

... raf> cargo run -- folder ./tests/test_files -t emails ip
    Finished dev [unoptimized + debuginfo] target(s) in 0.26s
     Running `target\debug\raf.exe folder ./tests/test_files -t emails ip`
error: Found argument 'ip' which wasn't expected, or isn't valid in this context

USAGE:
    raf.exe folder [OPTIONS] <PATH>

For more information try --help
error: process didn't exit successfully: `target\debug\raf.exe folder ./tests/test_files -t emails ip` (exit code: 2)

How do I make -t emails, ip to translate to FolderOpts.types = vec!["emails", "ip"]?

Jim
  • 450
  • 2
  • 10

2 Answers2

0

For multiple arguments to the same option in clap 4.0 or newer you can use num_args:

use clap::Parser;
#[derive(Debug, Parser)]
struct Opts {
    #[arg(short, long, num_args = 1..)]
    types: Vec<String>,
}

fn main() {
    let o = Opts::parse_from(["program_name", "-t", "first", "second"]);
    dbg!(o);
}

output:

[src/main.rs:10] o = Opts {
    types: [
        "first",
        "second",
    ],
}

Playground

cafce25
  • 15,907
  • 4
  • 25
  • 31
  • In that case you're using an old version of `clap` and should specify the exact version in your question (or just update to 4.*). – cafce25 Mar 26 '23 at 18:20
  • My `clap` version is `3.2.8`, upgrading to `4.x.x` gives me problem with parsing a `path` field as an `OsStr`, do you have any idea how I can fix it? – Jim Mar 26 '23 at 18:34
  • About the `OsStr` they [work for me](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=b4ee20519f48f5f54c659b94b58b99b1) if you can't figure it out you could ask a new question. – cafce25 Mar 26 '23 at 18:45
0

For clap:3.2.8, the following works:

#[derive(Args, Debug)]
pub struct FileOpts {
    #[clap(parse(from_os_str), required=true)]
    pub path: std::path::PathBuf,
    #[clap(short, long, required=true, multiple_values=true)]
    pub types: Vec<String>,
}

You'll have to set multiple_values to be true in the derive macro.

Jim
  • 450
  • 2
  • 10