With this script, I'm able to pass arguments as:
cargo run -- i
// or
trs -- i
use anyhow::{Context, Result};
use clap::{Parser, ValueEnum};
use regex::Regex;
use std::io::{BufRead, BufReader};
#[derive(Parser)]
#[clap(author, version, about)]
struct Cli {
/// Choose a command: 'i' for incrementing numbers, 'l' for incrementing letters
#[clap(value_enum)]
command: Command,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
enum Command {
I,
L,
}
struct TextTransformer {
text: String,
}
impl TextTransformer {
fn new<R: BufRead>(mut reader: R) -> Result<Self> {
let mut text = String::new();
let mut line = String::new();
while reader
.read_line(&mut line)
.with_context(|| "Failed to read line from stdin")?
> 0
{
text.push_str(&line);
line.clear();
}
Ok(Self { text })
}
fn increment_numbers(mut self) -> Self {
// Update regex to match numbers within alphanumeric strings
let number_regex = Regex::new(r"(\d+(\.\d+)?)").unwrap();
self.text = number_regex
.replace_all(&self.text, |caps: ®ex::Captures| {
let num = caps[1].parse::<f64>().unwrap();
(num + 1.0).to_string()
})
.to_string();
self
}
fn increment_letters(mut self) -> Self {
// Regex to match alphabetic characters
let letter_regex = Regex::new(r"[A-Za-z]").unwrap();
self.text = letter_regex
.replace_all(&self.text, |caps: ®ex::Captures| {
let c = caps[0].chars().next().unwrap();
let incremented = match c {
'a'..='y' | 'A'..='Y' => (c as u8 + 1) as char,
'z' => 'a',
'Z' => 'A',
_ => c,
};
incremented.to_string()
})
.to_string();
self
}
fn finalize(self) -> String {
self.text
}
}
fn main() -> Result<()> {
let cli = Cli::parse();
let stdin = std::io::stdin();
let reader = BufReader::new(stdin.lock());
let output = match cli.command {
Command::I => TextTransformer::new(reader)?.increment_numbers().finalize(),
Command::L => TextTransformer::new(reader)?.increment_letters().finalize(),
};
print!("{}", output);
Ok(())
}
But if I try to do something like trs -i
, I get this error:
Shell error: error: unexpected argument '-i' found tip: to pass '-i' as a value, use '-- -i'
Why is this?
I want the trs -i
to work, not the trs i
.
A command field of the form
#[clap(value_enum)]
command: Command,
Describes a positional argument, the value of which needs to match one of the enum variants. With variants I
and L
, Cli
will expect either i
or l
at the first argument position, and not both. Multi-letter values are supported in this position.
Arguments with a preceding -
or --
are conventionally for flags and options. Since this argument was not specified as a flag, it does not expect -i
or -l
.
As this is an attempt at implementing subcommands, the most idiomatic way to achieve what you want is:
command
as a subcommand instead of an argument with a fixed list of choices;short_flag
and/or long_flag
to the subcommands available.#[derive(Parser)]
struct Cli {
#[clap(subcommand)]
command: Command,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Parser)]
enum Command {
/// increment numbers
#[clap(short_flag('i'))]
I,
/// increment letters
#[clap(short_flag('l'))]
L,
}
The resulting help output:
Usage: trs <COMMAND>
Commands:
i, -i increment numbers
l, -l increment letters
help Print this message or the help of the given subcommand(s)
Options:
-h, --help Print help