Expand description
§aopt
A flexible and typed getopt like command line framwork for rust.
§Features
-
Option support
-
Prefixed option support, such as
-f,--flag,-flagor--/flag. -
Option value support, such as
-f 42,--flag 3.14or--flag=foo. -
Multiple style option support, such as
-f 42,-f=42or-f42. -
Combing style support, such as
-abcis same as-a-b-c. -
Positional arguments support, see
Index. -
Type support, you can validator the value of option during parsing.
See the built-in option type
AOpt -
-
Non UTF8 arguments support
-
Callback support
Can set callback which will called during parsing, see
ParserandInvoker. -
Value support
By default aopt will saving the raw value and parsed value in
ValStorer. -
Policy support
-
DelayPolicyprocess positional arguments before any other option. -
FwdPolicyprocess options before positional arguments. -
SeqPolicyprocess all type arguments one by one.
-
-
Derive support
- Checkout
cotecrate for derive support and help message generate.
- Checkout
§Setup
cargo add aopt
§sync feature
If you want the utils of current crate implement Send and Sync, you can enable sync feature.
§Examples
§Using AFwdParser parsing process the command line.
use aopt::prelude::*;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut parser = AFwdParser::default();
parser.validator_mut().add_prefix("+");
parser.add_opt("--depth=i")?.set_value_t(0i64); // int option
parser.add_opt("-/r=b")?; // boolean flag
parser
.add_opt("--source=s!")? // ! means the option is force required
.add_alias("+S")
.on(|set, ctx| {
let val = ctx.value::<String>()?;
let depth: &i64 = set["--depth"].val()?;
println!("Adding location({}) with depth({})", val, depth);
Ok(Some((val, *depth)))
})?;
parser.add_opt("destination=p!@-0")?.on(|_, ctx| {
let val = ctx.value::<String>()?;
println!("Save destination location({})", val);
Ok(Some(val))
})?;
parser.add_opt("main=m")?.on(|set, ctx| {
let val = ctx.value::<String>()?;
let src = set["--source"].vals::<(String, i64)>()?;
let dest: &String = set["destination"].val()?;
for (item, depth) in src {
println!(
"Application {} will copy location({item}, depth={depth}) to destination({})",
val, dest
);
}
Ok(Some(val))
})?;
parser.parse_env()?.unwrap();
Ok(())
}app.exe --depth=98 +S github --depth=42 +S gitlab gitcodeoutput
Adding location(github) with depth(98)
Adding location(gitlab) with depth(42)
Save destination location(gitcode)
Application target\debug\example.exe will copy location(github, depth=98) to destination(gitcode)
Application target\debug\example.exe will copy location(gitlab, depth=42) to destination(gitcode)§Using getopt! parsing multiple sub command.
use aopt::prelude::*;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut list = AFwdParser::default();
let mut update = AFwdParser::default();
let mut install = AFwdParser::default();
list.add_opt("list=c")?;
list.add_opt("ls=c")?;
list.add_opt("-debug=b")?;
list.add_opt("-force=b")?.add_alias("-f");
list.add_opt("-local-only=b")?.add_alias("-l");
list.add_opt("-source".infer::<String>())?
.add_alias("-s")
.set_value(String::from("lib.rs"));
list.add_opt("main=m")?.fallback(|set, _| {
println!(
"invoke list command: debug={:?}, force={:?}, local-only={:?}, source={:?}",
set["-debug"].val::<bool>()?,
set["-force"].val::<bool>()?,
set["-local-only"].val::<bool>()?,
set["-source"].val::<String>()?,
);
Ok(None::<()>)
})?;
update.add_opt("update=c")?;
update.add_opt("up=c")?;
update.add_opt("-debug=b")?;
update.add_opt("-force=b")?.add_alias("-f");
update.add_opt("-source=s")?.add_alias("-s");
update.add_opt("main=m")?.on(|set, _| {
println!(
"invoke update command: debug={:?}, force={:?}, source={:?}",
set["-debug"].val::<bool>()?,
set["-force"].val::<bool>()?,
set["-source"].val::<String>()?,
);
Ok(Some(true))
})?;
install.add_opt("install=c")?;
install.add_opt("in=c")?;
install.add_opt("-debug=b")?;
install.add_opt("-/override=b")?.add_alias("-/o");
install.add_opt("-source=s")?.add_alias("-s");
install.add_opt("name=p!@2")?.on(|set, ctx| {
let val = ctx.value::<String>()?;
if val == "software" {
println!(
"invoke install command: debug={:?}, override={:?}, source={:?}",
set["-debug"].val::<bool>()?,
set["-/override"].val::<bool>()?,
set["-source"].val::<String>()?,
);
Ok(Some(val))
} else {
Err(aopt::error!("command not matched"))
}
})?;
getopt!(Args::from_env(), &mut list, &mut update, &mut install)?;
Ok(())
}-
app.exe ls -debugoutputinvoke list command: debug=true, force=false, local-only=false, source=“lib.rs”
-
app.exe update -force -source=crates.iooutputinvoke update command: debug=false, force=true, source=“crates.io”
-
app.exe in software -/o -s crates.iooutputinvoke install command: debug=false, override=true, source=“crates.io”
-
app.exe in aoptoutputError: command not matched
§Can support different policy
use aopt::prelude::*;
use aopt::error;
use aopt::value::raw2str;
use aopt::Error;
fn main() -> Result<(), Box<dyn std::error::Error>> {
#[derive(Debug, Default, PartialEq, Eq)]
pub enum Magic {
Upper,
#[default]
Lower,
Camel,
}
impl Magic {
pub fn piu(&self, val: &str) -> String {
match self {
Magic::Upper => val.to_uppercase(),
Magic::Lower => val.to_lowercase(),
Magic::Camel => {
let val = val.to_lowercase();
let mut chars = val.chars();
format!(
"{}{}",
chars.next().unwrap_or_default().to_uppercase(),
chars.collect::<String>()
)
}
}
}
}
// Implement RawValParser for type Magic
impl RawValParser for Magic {
type Error = Error;
fn parse(raw: Option<&OsStr>, _: &Ctx) -> Result<Self, Self::Error> {
let mode = raw2str(raw)?.to_lowercase();
match mode.as_str() {
"upper" => Ok(Self::Upper),
"lower" => Ok(Self::Lower),
"camel" => Ok(Self::Camel),
_ => Err(error!("What do you want? wahaha")),
}
}
}
let mut set = AHCSet::default();
// using MutOpt for type only implement RawValParser
set.add_opt("-m;--magic".infer::<MutOpt<Magic>>())?;
set.add_opt("file@*".infer::<Pos<String>>())?
.on(|set, ctx| {
let val = ctx.value::<String>()?;
let magic = set.take_val::<Magic>("-m").unwrap_or_default();
Ok(Some(magic.piu(&val)))
})?;
let args = [
"app", "-mcamel", "html", "--magic", "lower", "png", "TXT", "-m=upper", "mp4",
];
// using seq policy
let mut seq = ASeqPolicy::default();
assert!(set.parse_policy(Args::from(args), &mut seq)?.ok().is_ok());
assert_eq!(
set.take_vals::<String>("file")?,
&["Html", "png", "txt", "MP4"]
);
// using fwd policy
let mut fwd = AFwdPolicy::default();
assert!(set.parse_policy(Args::from(args), &mut fwd)?.ok().is_ok());
assert_eq!(
set.take_vals::<String>("file")?,
&["HTML", "png", "Txt", "mp4"]
);
// using delay policy
let mut delay = ADelayPolicy::default();
assert!(set.parse_policy(Args::from(args), &mut delay)?.ok().is_ok());
assert_eq!(
set.take_vals::<String>("file")?,
&["html", "png", "txt", "mp4"]
);
Ok(())
}§Simple flow chart
+---------------------------------------+
| Policy |
| |
+--------------+ | +-----------+ +------------+ | +-------------+
| | | | | | | | Invoke | |
| Arguments +---->| | Checker | | Process |<----+----------------+ Invoker |
| | | | | | | | the callback | |
+--------------+ | +---^-------+ ++-----^-----+ | +-------------+
| | | | |
| | | | |
+------+--------------+-----+-----------+
| | |
| | |
| Save the values |Process the arguments
| | |
| | |
Check the options | |
| | |
| | |
| +----v-----+-----------+
| | |
+---------+ Option Set |
| |
+----------------------+§More examples
-
simple-find-file
A simple file search tools, try it using cargo install --path simple-find-file.
- snowball-follow
Get the follow count of stock in xueqiu.com, try it using cargo install --path snowball-follow
- index constituent
Search and list the constituent of index, try it using cargo install --path index-constituent
§Release log
Follow the link.
§LICENSE
MPL-2.0
Modules§
Macros§
- ctors
- Return an array of creators:
- error
- failure
- getopt
- Parse the string sequence with given
Parser. - trace
- Constructs an event at the trace level.