Module cote::_reference
source · Expand description
§Documentation: Cote Tutorial
- Quick Start
- Configurating Struct
- Configurating Field
- Configurating Options, Command flags and Positionals
- Configurating Sub Commands
- How it works
§Quick Start
Using Cote
derive you can quick setup a application.
use cote::prelude::*;
use std::path::PathBuf;
#[derive(Debug, Cote)]
#[cote(
name = "cli", // set the name of usage
help, // generate help and display help when `--help` set
aborthelp, // display help if any error raised
width = 50
)]
pub struct Cli {
/// Print debug message
#[arg(alias = "-d")]
debug: bool,
/// Set the configuration path
#[arg(alias = "-c", value = "default.json", hint = "-c,--config [CFG]")]
config: Option<PathBuf>,
/// Search the given directory
#[sub(name = "se")]
search: Option<Search>,
/// List the given directory
#[sub(name = "ls", head = "List the given directory")]
list: Option<List>,
}
#[derive(Debug, Cote)]
#[cote(help)]
pub struct Search {
/// Set the depth of search
depth: usize, // without `Option` mean force required
#[pos(value = ".", help = "Set the clean directory")]
dest: Option<PathBuf>,
}
#[derive(Debug, Cote)]
#[cote(help)]
pub struct List {
/// Enable recursive mode
recursive: bool,
#[pos(value = ".", help = "Set the clean directory")]
dest: Option<PathBuf>,
}
fn main() -> color_eyre::Result<()> {
color_eyre::install()?;
let cli = Cli::parse_env()?;
if cli.debug {
println!("enable debug mode, will print debug message")
}
if let Some(cfg) = cli.config.as_deref() {
println!("loading config from {:?}", cfg);
}
if let Some(list) = cli.list.as_ref() {
println!(
"list the directory `{:?}` with recursive({})",
list.dest.as_deref(),
list.recursive
);
} else if let Some(search) = cli.search.as_ref() {
println!(
"search the file under directory `{:?}` with depth {}",
search.dest.as_deref(),
search.depth
);
}
Ok(())
}
§Help message generate
- Output of
cli --help
:
Usage: cli [-h,-?,--help] [-d,--debug] [-c,--config [CFG]] <COMMAND>
Generate help message for command line program
Commands:
se@1 Search the given directory
ls@1 List the given directory
Options:
-h,-?,--help Display help message
-d,--debug Print debug message
-c,--config [CFG] Set the configuration path ["default.json"]
Create by araraloren <blackcatoverwall@gmail.com> v0.1.8
- Output of
cli ls --help
:
Usage: cli ls [-h,-?,--help] [--recursive] [ARGS]
List the given directory
Options:
-h,-?,--help Display help message
--recursive Enable recursive mode
Args:
dest@1 Set the list directory ["."]
Create by araraloren <blackcatoverwall@gmail.com> v0.1.8
§Running
Output of cli se --depth 2
:
loading config from "default.json"
search the file under directory `Some(".")` with depth 2
§aborthelp
If code generate with cote configuration aborthelp
.
When the option match failed, program will first
print help message, then display the error message.
Output of cli se --depth www
or cli se --depth
:
Usage: cli [-h,-?,--help] [-d,--debug] [-c,--config [CFG]] <COMMAND>
Generate help message for command line program
Commands:
se@1 Search the given directory
ls@1 List the given directory
Options:
-h,-?,--help Display help message
-d,--debug Print debug message
-c,--config [CFG] Set the configuration path ["default.json"]
Create by araraloren <blackcatoverwall@gmail.com> v0.1.8
Error:
0: Parsing command `se` failed: InnerCtx { uid: 1, name: Some(--depth), style: Style::Argument, arg: Some("www"), index: 1, total: 3 }
1: Can not find option `--depth`
2: Can not convert value `www` to usize
3: invalid digit found in string
Location:
src\main.rs:82
Backtrace omitted.
Run with RUST_BACKTRACE=1 environment variable to display it.
Run with RUST_BACKTRACE=full to include source snippets.
§Configurating Struct
§Configurating Policy
Cote has three policy types built-in: fwd
、pre
and delay
.
If no policy
configuration specific, fwd
will be using if no sub command.
Otherwise pre
will be used.
use cote::prelude::*;
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(policy = delay)] // set policy to delay
pub struct Cli {
debug: bool,
}
fn main() -> color_eyre::Result<()> {
color_eyre::install()?;
let CoteRes {
policy, mut parser, ..
} = Cli::parse_env_args()?;
assert_eq!(policy.no_delay().map(|v| v.len()), Some(0));
assert_eq!(Cli::try_extract(parser.optset_mut())?, Cli { debug: false });
Ok(())
}
§Configurating Help
Specify help
in cote
attribute will automate generate help message for current application.
And aborthelp
will automate display the help message if any error raised.
The default name of the application is the name of the current package,
i.e., the result of String::from(env!("CARGO_PKG_NAME"))
.
You also can custom it with name
.
The default maximum length of the option help message is 40, use width
custom it.
The default maximum count of usage option item is 10, use usagew
custom it.
The text set by head
will display after usage, in default it is description of package,
i.e., the result of String::from(env!("CARGO_PKG_DESCRIPTION"))
.
The text set by foot
will display at the bottom, in default it is result of
format!("Create by {} v{}", env!("CARGO_PKG_AUTHORS"), env!("CARGO_PKG_VERSION"))
.
§Example
use cote::prelude::*;
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(help, // Generate help for current struct
aborthelp, // Display help when error raised
name = "app", // Set the usage name
width = 50, // Set the maximum width of option help message
usagew = 3, // Set the maximum count of item in usage
head = "The head message display in help message",
foot = "The foot message display in help message",
)]
pub struct Cli {
/// Print debug message.
debug: bool,
/// Set the name of client.
name: String,
/// Switch to foo sub command.
foo: Cmd,
/// Switch to bar sub command.
bar: Cmd,
/// The second position argument.
#[pos(index = "2")]
arg: String,
/// Collection of arguments start from position 3.
#[pos(index = 3..)]
args: Vec<String>,
}
fn main() -> color_eyre::Result<()> {
color_eyre::install()?;
// pass `--help` to program display help message
Cli::parse(Args::from(["app", "--help"]))?;
Ok(())
}
The help message output like this:
Usage: app [-h,-?,--help] [--debug] <--name>
<COMMAND> [ARGS]
The head message display in help message
Commands:
foo@1 Switch to foo sub command.
bar@1 Switch to bar sub command.
Options:
-h,-?,--help Display help message
--debug Print debug message.
--name Set the name of client.
Args:
arg@2 The second position argument.
args@3.. Collection of arguments start from position 3.
The foot message display in help message
§Configurating User Style
The option styles support by default are:
Options such as --opt=value
, the value of option is set after =
.
Options such as --opt value
, the value of option is next argument.
Options such as -o42
, the value 42
of option is embedded in string.
The style only support one letter option.
Options such as --opt
, in general, it is named flag, the value type of option is always bool
.
§Other available User Styles
combine
- Add support forCombinedOption
.
Options such as -abcd
, thus set both boolean options -a
, -b
, -c
and -d
.
use cote::prelude::*;
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(combine)]
pub struct Cli {
#[arg(alias = "-d")]
debug: bool,
#[arg(alias = "-r")]
recursive: bool,
#[arg(alias = "-f")]
force: bool,
}
fn main() -> Result<(), aopt::Error> {
// set three options in one item
let cli = Cli::parse(Args::from(["app", "-rdf"]))?;
assert!(cli.debug);
assert!(cli.recursive);
assert!(cli.force);
Ok(())
}
embedded
- Add support forEmbeddedValuePlus
.
Options such as --opt42
, the value 42
of option is embedded in string.
The style only supports options which name lengths bigger than 2.
use cote::prelude::*;
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(embedded)]
pub struct Cli {
foo: String,
}
fn main() -> Result<(), aopt::Error> {
let cli = Cli::parse(Args::from(["app", "--foobar"]))?;
assert_eq!(cli.foo, "bar");
Ok(())
}
flag
- Add support forFlag
.
Options such as --opt
, in general, it is named flag, the value type of option is always bool
.
Unlike Boolean
pass TRUE
to parse
,
Flag
pass None
to parse
.
use cote::prelude::*;
#[derive(Debug, Cote)]
#[cote(help, flag)]
pub struct Cli {
flag: Option<Flag>,
}
#[derive(Debug, Default, PartialEq, Eq)]
pub struct Flag;
impl Infer for Flag {
type Val = Flag;
fn infer_style() -> Vec<aopt::prelude::Style> {
vec![Style::Flag]
}
}
cote::impl_alter!(Flag);
cote::impl_fetch!(Flag);
impl RawValParser for Flag {
type Error = cote::Error;
fn parse(raw: Option<&RawVal>, _: &Ctx) -> Result<Self, Self::Error> {
assert!(raw.is_none());
Ok(Flag {})
}
}
fn main() -> color_eyre::Result<()> {
color_eyre::install()?;
let cli = Cli::parse(Args::from(["app", "--flag"].into_iter()))?;
assert_eq!(cli.flag, Some(Flag {}));
Ok(())
}
§Configurating Field
§Options
In default or specific the attribute arg
, the fields of struct are generated into options.
use cote::prelude::*;
#[derive(Debug, Cote, PartialEq, Eq)]
pub struct Cli {
foo: Option<String>, // In default, it is generated into options.
#[arg(name = "-b")]
bar: Option<String>,
}
fn main() -> Result<(), aopt::Error> {
let cli = Cli::parse(Args::from(["app"]))?;
assert_eq!(cli.foo.as_deref(), None);
assert_eq!(cli.bar.as_deref(), None);
let cli = Cli::parse(Args::from(["app", "--foo", "bar", "-b=foo"]))?;
assert_eq!(cli.foo.as_deref(), Some("bar"));
assert_eq!(cli.bar.as_deref(), Some("foo"));
let cli = Cli::parse(Args::from(["app", "-b", "foo", "--foo=bar"]))?;
assert_eq!(cli.foo.as_deref(), Some("bar"));
assert_eq!(cli.bar.as_deref(), Some("foo"));
Ok(())
}
§Positionals
Specific the attribute pos
if you want to match the command line arguments by position.
use cote::prelude::*;
#[derive(Debug, Cote, PartialEq, Eq)]
pub struct Cli {
#[pos()] // index will set to 1
foo: Option<String>, // if not specific, index will automate generated
#[pos(index = "2")]
bar: Option<String>,
}
fn main() -> Result<(), aopt::Error> {
let app = Cli::into_parser()?;
assert_eq!(app["foo"].index(), Some(&Index::forward(1)));
assert_eq!(app["bar"].index(), Some(&Index::forward(2)));
let cli = Cli::parse(Args::from(["app"]))?;
assert_eq!(cli.foo.as_deref(), None);
assert_eq!(cli.bar.as_deref(), None);
let cli = Cli::parse(Args::from(["app", "42", "foo"]))?;
assert_eq!(cli.foo.as_deref(), Some("42"));
assert_eq!(cli.bar.as_deref(), Some("foo"));
Ok(())
}
§Command Flags
Specific the attribute cmd
will let you create a sub command flag.
use cote::prelude::*;
#[derive(Debug, Cote, PartialEq, Eq)]
pub struct Cli {
#[cmd()]
foo: bool, // Command flag has a fixed position 1,
// and it's always force required
#[pos(index = "2")]
bar: Option<String>,
}
fn main() -> Result<(), aopt::Error> {
let app = Cli::into_parser()?;
assert_eq!(app["foo"].index(), Some(&Index::forward(1)));
assert_eq!(app["bar"].index(), Some(&Index::forward(2)));
let cli = Cli::parse(Args::from(["app", "foo", "42"]))?;
assert_eq!(cli.bar.as_deref(), Some("42"));
assert!(Cli::parse(Args::from(["app", "42", "foo"])).is_err());
assert!(Cli::parse(Args::from(["app", "42"])).is_err());
Ok(())
}
§Sub Commands
Specific the attribute sub
will let you create a sub commands.
use cote::prelude::*;
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(help, aborthelp)]
pub struct Cli {
#[arg()]
bar: usize,
#[sub(alias = "z")]
baz: Option<Baz>,
#[sub(alias = "x")]
qux: Option<Qux>,
}
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(help, aborthelp)]
pub struct Baz {
grault: bool,
waldo: Option<String>,
}
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(help, aborthelp)]
pub struct Qux {
garply: bool,
fred: String,
}
fn main() -> color_eyre::Result<()> {
color_eyre::install()?;
let cli = Cli::parse(Args::from(["app", "--bar=42", "z"]))?;
assert_eq!(cli.bar, 42);
assert_eq!(
cli.baz,
Some(Baz {
grault: false,
waldo: None
})
);
assert_eq!(cli.qux, None);
let cli = Cli::parse(Args::from(["app", "--bar=42", "x", "--fred", "plugh"]))?;
assert_eq!(cli.bar, 42);
assert_eq!(cli.baz, None);
assert_eq!(
cli.qux,
Some(Qux {
garply: false,
fred: "plugh".to_owned()
})
);
Ok(())
}
§Configurating Options, Command flags and Positionals
§Configurating the name and alias
The default name of positionals and command flags is the name of the field.
The default name of options consists of prefixs and identifiers of the field.
The default prefix is --
if count of characters bigger than 1, otherwise -
is using.
You can use name
or alias
configure the name and alias of the option.
For prefix information reference PrefixOptValidator
.
use cote::prelude::*;
#[derive(Debug, Cote, PartialEq, Eq)]
pub struct Cli {
#[cmd(name = "foo", alias = "f")]
cmd: bool,
// set the name of position, for access the option from index operator
#[pos(name = "bar", index = "2")]
pos: usize,
// set the option name with prefix
#[arg(name = "--baz", alias = "-b")]
opt: String,
}
fn main() -> color_eyre::Result<()> {
color_eyre::install()?;
let app = Cli::into_parser()?;
assert_eq!(app["foo"].name(), "foo");
assert_eq!(app["bar"].name(), "bar");
assert_eq!(app["--baz"].name(), "--baz");
assert_eq!(app["-b"].name(), "--baz");
let cli = Cli::parse(Args::from(["app", "--baz", "qux", "foo", "42"]))?;
assert!(cli.cmd);
assert_eq!(cli.pos, 42);
assert_eq!(cli.opt, "qux");
let cli = Cli::parse(Args::from(["app", "f", "-b=quux", "88"]))?;
assert!(cli.cmd);
assert_eq!(cli.pos, 88);
assert_eq!(cli.opt, "quux");
Ok(())
}
§Configurating the hint, help and default value
Hint is displayed on usage or the left side of item information.
In default, hint message is generated from the name and alias of item,
use hint
custom the hint information of item.
Help is displayed on the right side of item information.
Use help
configure the help information of item.
The default values will be display in help message if it is set.
use aopt::prelude::AOpt;
use cote::prelude::*;
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(help)]
pub struct Cli {
/// Switch the mode to foo command
#[cmd()]
foo: bool,
/// Set the value of bar
#[pos(index = "2", value = 42usize, hint = "[BAR]")]
bar: Option<usize>,
#[arg(alias = "-b", help = "Set the string value of baz")]
baz: String,
#[pos(index = 3.., values = ["corge", "grault"])]
quux: Vec<String>,
}
// Access the default value need invoke initialize handler, not recommend do this
fn default_value<T: ErasedTy>(opt: &mut AOpt) -> cote::Result<Option<Vec<T>>> {
opt.accessor_mut().initializer_mut().values::<T>()
}
fn main() -> color_eyre::Result<()> {
color_eyre::install()?;
let mut app = Cli::into_parser()?;
assert_eq!(app["foo"].hint(), "foo@1");
assert_eq!(app["bar"].hint(), "[BAR]");
assert_eq!(app["--baz"].hint(), "-b, --baz");
assert_eq!(app["foo"].help(), "Switch the mode to foo command");
assert_eq!(app["bar"].help(), "Set the value of bar [42]");
assert_eq!(app["--baz"].help(), "Set the string value of baz");
assert_eq!(default_value::<String>(&mut app["--baz"])?, None);
assert_eq!(default_value::<usize>(&mut app["bar"])?, Some(vec![42]));
assert_eq!(
default_value::<String>(&mut app["quux"])?,
Some(vec!["corge".to_owned(), "grault".to_owned()])
);
// Currently only display default values are set in the attribute
Cli::parse(Args::from(["app", "--help"]))?;
Ok(())
}
Running the code, it’s output should be:
Usage: cli [-h,-?,--help] <-b,--baz> <COMMAND> [ARGS]
Generate help message for command line program
Commands:
foo@1 Switch the mode to foo command
Options:
-h,-?,--help Display help message
-b,--baz Set the string value of baz
Args:
[BAR] Set the value of bar [42]
quux@3..
Create by araraloren <blackcatoverwall@gmail.com> v0.1.8
§Configurating the index
Index is only support positions and command flags.
For command flags, the index is fixed position @1
by default.
For more informations about index, reference Index
.
§Example1
use cote::prelude::*;
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(help)]
pub struct Cli {
// `cmd` has a fixed position in default, you can't change it
// and you can't both have a `cmd` and a `pos` at index 1
#[cmd()]
foo: bool,
// `bar` has a index 2
#[pos(index = "2", value = 42usize, hint = "[BAR]")]
bar: Option<usize>,
// option ignore the index value when matching with command line arguments
#[arg(alias = "-b", help = "Set the string value of baz")]
baz: String,
// `quux` can accept position arguments at range from 3 to infinite
#[pos(index = 3.., values = ["corge", "grault"])]
quux: Vec<String>,
}
fn main() -> color_eyre::Result<()> {
color_eyre::install()?;
let app = Cli::into_parser()?;
assert_eq!(app["foo"].index(), Some(&Index::forward(1)));
assert_eq!(app["bar"].index(), Some(&Index::forward(2)));
assert_eq!(app["--baz"].index(), None);
assert_eq!(app["quux"].index(), Some(&Index::range(Some(3), None)));
Ok(())
}
§Example2
For the item configured by pos
, the index is automating generated start form 1
if no index set.
use cote::prelude::*;
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(help)]
pub struct Cli {
// `bar` has an index 1, it is automated generate by derive macro
#[pos(value = 42usize)]
bar: Option<usize>,
// option ignore the index value when matching with command line arguments
#[arg(alias = "-b", help = "Set the string value of baz")]
baz: String,
// `quux` can accept position arguments at range 3 or 4
#[pos(index = 3..5)]
quux: Vec<String>,
}
fn main() -> color_eyre::Result<()> {
color_eyre::install()?;
let parser = Cli::into_parser()?;
assert_eq!(parser["bar"].index(), Some(&Index::forward(1)));
assert_eq!(parser["--baz"].index(), None);
assert_eq!(
parser["quux"].index(),
Some(&Index::range(Some(3), Some(5)))
);
let app = Cli::parse(Args::from([
"app", // index 0
"88", // index 1
"--baz", // option --baz
"foo", // value of option --baz
"ignore", // index 2
"what", // index 3
"where", // index 4
]))?;
assert_eq!(app.bar, Some(88));
assert_eq!(app.baz, "foo");
assert_eq!(app.quux, vec!["what".to_owned(), "where".to_owned()]);
Ok(())
}
§Force required Positionals and Options
In default, options, positionals and command flags is force required.
Wrap the type with Option
can make the item optional.
Using force
you can configure the positionals and options force required.
use cote::prelude::*;
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(help)]
pub struct Cli {
// `cmd` is force required in default, you can't change it
#[cmd()]
foo: bool,
// `Option` make the `pos` optional in default
#[pos(index = "2", value = 42usize)]
bar: Option<usize>,
// Without `Option`, `--baz` is force required
#[arg(alias = "-b", help = "Set the string value of baz")]
baz: String,
// Using `force` you can force set the option to force required
#[arg(force = true)]
qux: Option<i64>,
// If the option has default value, then it is optional
#[arg(values = ["need"])]
quux: Vec<String>,
}
fn main() -> color_eyre::Result<()> {
color_eyre::install()?;
assert!(Cli::parse(Args::from(["app", "--baz=6"])).is_err());
assert!(Cli::parse(Args::from(["app", "foo", "--baz=6"])).is_err());
assert!(Cli::parse(Args::from(["app", "--qux", "-5", "foo", "--baz=6"])).is_ok());
Ok(())
}
§Configurating action
The type that implements Infer
has different Action
.
The Action
defines the behavior when saving the value.
For more information, see Action::process
and AOpt
.
use cote::prelude::*;
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(help)]
pub struct Cli {
// bool default has Action::Set
#[arg(ty = bool, action = Action::Cnt)]
foo: u64,
// usize default has Action::App
#[arg(action = Action::Set)]
bar: usize,
}
fn main() -> color_eyre::Result<()> {
color_eyre::install()?;
let cli = Cli::parse(Args::from([
"app", "--foo", "--foo", "--bar=42", "--bar=88",
]))?;
assert_eq!(cli.foo, 2);
assert_eq!(cli.bar, 88);
Ok(())
}
§Configurating handler
Using on
, fallback
attribute configure the handler which will be called when
option set.
Using then
attribute configure the store behavior when saving value.
-
on
cote
Will be invoked if struct parsed successfully. Because the name of
Main
option will be generate automate. So you can’t get the return value currently.arg
orpos
Will be invoked if option set by user. The return value will be saved as value of option.
sub
Not support, set the handler on struct type using
cote
. -
fallback
Same as on
except if the handler returns Ok(None)
, the default handler will be invoked.
then
Using with on
and fallback
, do nothing without on
and fallback
.
It will responded for saving the raw value and value.
use cote::prelude::*;
use std::{fmt::Debug, ops::Deref};
// The handler must be a generic function.
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(help, on = display_cli)]
pub struct Cli {
#[arg(on = empty_handler, then = foo_storer)]
foo: u64,
#[sub(force = false)]
bar: Option<Bar>,
#[sub(force = false)]
qux: Option<Qux>,
}
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(help)]
pub struct Bar {
#[arg(force = false, fallback = debug_of_bar)]
debug: bool,
#[pos()]
quux: String,
}
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(help, fallback = process_qux, then = unreachable_storer)]
pub struct Qux {
#[cmd(name = "c")]
corge: bool,
grault: Option<i64>,
}
fn main() -> color_eyre::Result<()> {
color_eyre::install()?;
// unwrap the failure of return value
Cli::parse_env_args()?.ret.unwrap();
Ok(())
}
fn display_cli<Set, Ser>(set: &mut Set, _: &mut Ser) -> Result<Option<()>, aopt::Error>
where
Set: SetValueFindExt + cote::prelude::Set,
SetCfg<Set>: ConfigValue + Default,
{
println!("Got client: {:?}", Cli::try_extract(set)?);
Ok(None)
}
fn empty_handler<Set, Ser>(
_: &mut Set,
_: &mut Ser,
value: Option<ctx::Value<u64>>,
) -> Result<Option<u64>, aopt::Error> {
Ok(value.map(|mut v| v.take()))
}
fn foo_storer<Set, Ser>(
uid: Uid,
set: &mut Set,
_: &mut Ser,
raw: Option<&RawVal>,
val: Option<u64>,
) -> Result<bool, aopt::Error>
where
Set: SetValueFindExt + cote::prelude::Set,
SetCfg<Set>: ConfigValue + Default,
{
let has_value = val.is_some();
// Set the value if return Some(Value)
if let Some(val) = val {
if let Some(opt) = set.get_mut(uid) {
let (raw_handler, handler) = opt.accessor_mut().handlers();
if let Some(raw_value) = raw {
raw_handler.push(raw_value.clone());
}
println!("Saving the value of `--foo` to {}", val + 1);
// modify the value, plus one
handler.push(val + 1);
}
}
Ok(has_value)
}
fn debug_of_bar<Set, Ser>(
_: &mut Set,
_: &mut Ser,
raw: ctx::RawVal,
value: ctx::Value<bool>,
) -> Result<Option<()>, aopt::Error> {
println!(
"Got value of `--debug`: {:?} --> {}",
raw.deref(),
value.deref()
);
// if return None, the parser will call default handler of current option
Ok(None)
}
fn process_qux<Set, Ser>(_: &mut Set, _: &mut Ser) -> Result<Option<()>, aopt::Error>
where
Set: SetValueFindExt + cote::prelude::Set,
SetCfg<Set>: ConfigValue + Default,
{
println!("return Ok(None) call the default handler of Qux");
Ok(None)
}
fn unreachable_storer<Set, Ser>(
_: Uid,
_: &mut Set,
_: &mut Ser,
_: Option<&RawVal>,
_: Option<()>,
) -> Result<bool, aopt::Error>
where
Set: SetValueFindExt + cote::prelude::Set,
SetCfg<Set>: ConfigValue + Default,
{
unreachable!("Never go here")
}
- Output of command line
cli --foo 6
:
Saving the value of `--foo` to 7
Got client: Cli { foo: 7, bar: None, qux: None }
- Output of command line
cli --foo 8 bar a2i
:
Saving the value of `--foo` to 9
Got client: Cli { foo: 9, bar: Some(Bar { debug: false, quux: "a2i" }), qux: None }
- Output of command line
cli --foo 8 bar a2i --debug
:
Saving the value of `--foo` to 9
Got value of `--debug`: RawVal("true") --> true
Got client: Cli { foo: 9, bar: Some(Bar { debug: false, quux: "a2i" }), qux: None }
- Output of command line
cli --foo 9 qux c
:
Saving the value of `--foo` to 10
return Ok(None) call the default handler of Qux
Got client: Cli { foo: 9, bar: None, qux: Some(Qux { corge: true, grault: None }) }
- Output of command line
cli --foo 9 qux c --grault=42
:
Saving the value of `--foo` to 10
return Ok(None) call the default handler of Qux
Got client: Cli { foo: 9, bar: None, qux: Some(Qux { corge: true, grault: Some(42) }) }
§Validate values
You can using valid
check the value inside attribute.
Using valid!
generate struct implemented Validate
for the valid attribute.
use cote::prelude::*;
use cote::valid;
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(help)]
pub struct Cli {
#[arg(valid = valid!(42))]
foo: u64,
#[arg(valid = valid!(["qux", "quux"]))]
bar: Option<String>,
#[pos(valid = valid!(4..42))]
baz: Option<usize>,
}
fn main() -> color_eyre::Result<()> {
color_eyre::install()?;
assert!(Cli::parse(Args::from(["app", "--bar", "qux"])).is_err());
assert!(Cli::parse(Args::from(["app", "--bar", "baz", "--foo=0"])).is_err());
assert!(Cli::parse(Args::from(["app", "--bar", "baz", "68", "--foo=0"])).is_err());
let cli = Cli::parse(Args::from(["app", "--bar", "qux", "--foo=42"]))?;
assert_eq!(cli.foo, 42);
assert_eq!(cli.bar.as_deref(), Some("qux"));
let cli = Cli::parse(Args::from(["app", "--bar", "qux", "--foo=42", "6"]))?;
assert_eq!(cli.foo, 42);
assert_eq!(cli.bar.as_deref(), Some("qux"));
assert_eq!(cli.baz, Some(6));
Ok(())
}
§Add “no delay” option
When using DelayPolicy
, the option process(invoke handler)
after Cmd
and Pos
style.
Sometimes we need the option process like FwdPolicy
does,
that is process before Cmd
and Pos
.
use cote::prelude::*;
use std::ops::Deref;
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(policy = delay, help)]
pub struct Cli {
#[cmd(on = cmd_order)]
foo: bool,
#[arg(on = assert_order)]
bar: usize,
#[pos(on = assert_order, index = 2)]
baz: usize,
#[arg(on = assert_order, nodelay)]
qux: usize,
}
fn cmd_order<Set, Ser>(_: &mut Set, ser: &mut Ser) -> Result<Option<bool>, aopt::Error>
where
Ser: ServicesValExt,
{
let order = ser.sve_val_mut::<usize>()?;
*order += 1;
let order = *order;
assert_eq!(order, 2);
println!("Order {}", order);
Ok(Some(true))
}
fn assert_order<Set, Ser>(
_: &mut Set,
ser: &mut Ser,
mut val: ctx::Value<usize>,
) -> Result<Option<usize>, aopt::Error>
where
Ser: ServicesValExt,
{
let order = ser.sve_val_mut::<usize>()?;
*order += 1;
let order = *order;
println!("Order {}", order);
assert_eq!(order, *val.deref());
Ok(Some(val.take()))
}
fn main() -> color_eyre::Result<()> {
color_eyre::install()?;
let mut app = Cli::into_parser()?;
let mut policy = Cli::into_policy();
app.set_app_data(0usize)?;
app.run_mut_with(
["app", "foo", "--bar=4", "--qux=1", "3"].into_iter(),
&mut policy,
|_, app| {
let cli = Cli::try_extract(app.optset_mut())?;
assert!(cli.foo);
assert_eq!(cli.bar, 4);
assert_eq!(cli.qux, 1);
assert_eq!(cli.baz, 3);
Ok(())
},
)?;
Ok(())
}
§Configurating Sub Commands
Using sub
attribute define sub command.
use cote::prelude::*;
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(help, aborthelp)]
pub struct Cli {
#[arg(alias = "-g")]
age: usize,
/// Help message of eat sub command
#[sub()]
eat: Option<Eat>,
/// Help message of sport sub command
#[sub(policy = pre)]
sport: Option<Sport>,
}
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(help, aborthelp)]
pub struct Eat {
/// Which meal did you have?
#[arg(alias = "-m")]
meal: String,
/// What did you want?
#[pos(value = "rice")]
what: Option<String>,
}
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(help, aborthelp)]
pub struct Sport {
/// Go for a walk.
#[sub()]
walk: Option<Walk>,
/// Play some games.
#[sub()]
play: Option<Play>,
}
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(help, aborthelp)]
pub struct Walk {
#[arg(name = "-d", value = 3usize)]
distance: usize,
}
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(help, aborthelp)]
pub struct Play {
/// Which game do you want to play?
#[pos(value = "Mario")]
game: String,
}
fn main() -> color_eyre::Result<()> {
color_eyre::install()?;
let cli = Cli::parse_env()?;
println!("You age is set to {}", cli.age);
if let Some(eat) = cli.eat {
println!("You {} are going to eat {}", eat.meal, eat.what.unwrap());
} else if let Some(sport) = cli.sport {
if let Some(walk) = sport.walk {
println!("You are going to walk {} kilometers", walk.distance);
} else if let Some(play) = sport.play {
println!("You are going to play game {}", play.game);
}
}
Ok(())
}
§Configurating Policy
The default Policy
of sub command is FwdPolicy
.
For the sub commands to have sub commands, you should use PrePolicy
instead.
For example, sport
sub command does have two sub commands, it is configured with #[sub(policy = pre)]
.
Without policy = pre
, you will got output when running cli -g=42 sport walk -d 4
:
Usage: cli sport [-h,-?,--help] <COMMAND>
Generate help message for command line program
Commands:
walk@1 Go for a walk.
play@1 Play some games.
Options:
-h,-?,--help Display help message
Create by araraloren <blackcatoverwall@gmail.com> v0.1.8
Error:
0: Parsing command `sport` failed: None
1: Command `eat@1 | sport@1` are force required (uid = 1)
2: Can not find option `-d`
Location:
src\main.rs:90
Backtrace omitted.
Run with RUST_BACKTRACE=1 environment variable to display it.
Run with RUST_BACKTRACE=full to include source snippets.
And the right output should be:
You age is set to 42
You are going to walk 4 kilometers
§Configurating name and alias
Using name
and alias
you can configure the name and alias of sub commands in sub
attribute.
The name and alias will affect how to set the sub command and help message of sub command.
With follow change:
use cote::prelude::*;
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(help, aborthelp)]
pub struct Cli {
#[arg(alias = "-g")]
age: usize,
/// Help message of eat sub command
#[sub(alias = "e")]
eat: Option<Eat>,
/// Help message of sport sub command
#[sub(name = "sp", policy = pre)]
sport: Option<Sport>,
}
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(help, aborthelp)]
pub struct Eat {
/// Which meal did you have?
#[arg(alias = "-m")]
meal: String,
/// What did you want?
#[pos(value = "rice")]
what: Option<String>,
}
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(help, aborthelp)]
pub struct Sport {
/// Go for a walk.
#[sub()]
walk: Option<Walk>,
/// Play some games.
#[sub()]
play: Option<Play>,
}
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(help, aborthelp)]
pub struct Walk {
#[arg(name = "-d", value = 3usize)]
distance: usize,
}
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(help, aborthelp)]
pub struct Play {
/// Which game do you want to play?
#[pos(value = "Mario")]
game: String,
}
fn main() -> color_eyre::Result<()> {
color_eyre::install()?;
let cli = Cli::parse_env()?;
println!("You age is set to {}", cli.age);
if let Some(eat) = cli.eat {
println!("You {} are going to eat {}", eat.meal, eat.what.unwrap());
} else if let Some(sport) = cli.sport {
if let Some(walk) = sport.walk {
println!("You are going to walk {} kilometers", walk.distance);
} else if let Some(play) = sport.play {
println!("You are going to play game {}", play.game);
}
}
Ok(())
}
The output of commands cli -g22 e --help
is:
Usage: cli e [-h,-?,--help] <-m,--meal> [ARGS]
Generate help message for command line program
Options:
-h,-?,--help Display help message
-m,--meal Which meal did you have?
Args:
what@1 What did you wat? ["rice"]
Create by araraloren <blackcatoverwall@gmail.com> v0.1.8
§Configurating help message
Using hint
, help
, head
, foot
you can configure the help message of sub commands.
Just like those configures how work in cote
attribute, they can tweak the help message of sub commands.
use cote::prelude::*;
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(help, aborthelp)]
pub struct Cli {
#[arg(alias = "-g")]
age: usize,
/// Help message of eat sub command
#[sub(alias = "e")]
eat: Option<Eat>,
/// Help message of sport sub command
#[sub(policy = pre,
head = "This is head message of sport sub command.",
foot = "This is foot message of sport sub command."
)]
sport: Option<Sport>,
}
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(help, aborthelp)]
pub struct Eat {
/// Which meal did you have?
#[arg(alias = "-m")]
meal: String,
/// What did you wat?
#[pos(value = "rice")]
what: Option<String>,
}
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(help, aborthelp)]
pub struct Sport {
/// Go for a walk.
#[sub(hint = "[walk]")]
walk: Option<Walk>,
/// Play some games.
#[sub(hint = "[play]")]
play: Option<Play>,
}
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(help, aborthelp)]
pub struct Walk {
#[arg(name = "-d", value = 3usize)]
distance: usize,
}
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(help, aborthelp)]
pub struct Play {
/// Which game do you want to play?
#[pos(value = "Mario")]
game: String,
}
fn main() -> color_eyre::Result<()> {
color_eyre::install()?;
let cli = Cli::parse_env()?;
println!("You age is set to {}", cli.age);
if let Some(eat) = cli.eat {
println!("You {} are going to eat {}", eat.meal, eat.what.unwrap());
} else if let Some(sport) = cli.sport {
if let Some(walk) = sport.walk {
println!("You are going to walk {} kilometers", walk.distance);
} else if let Some(play) = sport.play {
println!("You are going to play game {}", play.game);
}
}
Ok(())
}
The output of commands cli -g8 sport --help
is:
Usage: cli sport [-h,-?,--help] <COMMAND>
This is head message of sport sub command.
Commands:
[walk] Go for a walk.
[play] Play some games.
Options:
-h,-?,--help Display help message
This is foot message of sport sub command.
§Optional Sub commands
The sub commands are force required in default.
Cote will raised an error if no sub command set.
Using force
make all sub commands optional avoid this error.
use cote::prelude::*;
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(help, aborthelp)]
pub struct Cli {
#[arg(alias = "-g")]
age: usize,
/// Help message of eat sub command
#[sub(alias = "e")]
eat: Option<Eat>,
/// Help message of sport sub command
#[sub(name = "sp", policy = pre)]
sport: Option<Sport>,
}
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(help, aborthelp)]
pub struct Eat {
/// Which meal did you have?
#[arg(alias = "-m")]
meal: String,
/// What did you wat?
#[pos(value = "rice")]
what: Option<String>,
}
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(help, aborthelp)]
pub struct Sport {
/// Go for a walk.
#[sub(force = false)]
walk: Option<Walk>,
/// Play some games.
#[sub(force = false)]
play: Option<Play>,
}
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(help, aborthelp)]
pub struct Walk {
#[arg(name = "-d", value = 3usize)]
distance: usize,
}
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(help, aborthelp)]
pub struct Play {
/// Which game do you want to play?
#[pos(value = "Mario")]
game: String,
}
fn main() -> color_eyre::Result<()> {
color_eyre::install()?;
let cli = Cli::parse_env()?;
println!("You age is set to {}", cli.age);
if let Some(eat) = cli.eat {
println!("You {} are going to eat {}", eat.meal, eat.what.unwrap());
} else if let Some(sport) = cli.sport {
if let Some(walk) = sport.walk {
println!("You are going to walk {} kilometers", walk.distance);
} else if let Some(play) = sport.play {
println!("You are going to play game {}", play.game);
}
}
Ok(())
}
Instead display the help and error message, the output of commands cli -g8 sp
is:
You age is set to 8
§How it works
§Traits
Implement follow traits, you can using the type in the struct filed.
Cote
using infer_fill_info
inference the default settings of
given type.
Cote
using fetch
fetch the value from Set
.
Cote
using parse
parsing the value from command line arguments.
Cote
using the trait override the action or optional behavior of Infer
.
type | action | force required | force required if has default value |
---|---|---|---|
T | Action::Set | true | false |
Option<T> | Action::Set | false | false |
Vec<T> | Action::App | true | false |
Option<Vec<T>> | Action::App | false | false |
Pos<T> | Action::Set | true | false |
bool | Action::Set | false | false |
Cmd | Action::Set | true | true |
§Example
The type Speed
base on the type i32
which already implemented RawValParser
.
use cote::prelude::*;
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(help, aborthelp)]
pub struct Cli {
#[arg(alias = "-s")]
speed: Speed,
}
#[derive(Debug, PartialEq, Eq)]
pub struct Speed(i32);
impl Infer for Speed {
type Val = i32;
}
impl<'a, S> Fetch<'a, S> for Speed
where
S: SetValueFindExt,
SetCfg<S>: ConfigValue + Default,
Self: ErasedTy + Sized,
{
fn fetch_uid(uid: Uid, set: &'a mut S) -> cote::Result<Self> {
Ok(Speed(fetch_uid_impl(uid, set)?))
}
}
impl Alter for Speed {
// using default behavior
}
fn main() -> color_eyre::Result<()> {
color_eyre::install()?;
let cli = Cli::parse(Args::from(["app", "--speed", "65"]))?;
assert_eq!(cli.speed.0, 65);
Ok(())
}
§Example - Derive default behavior from CoteOpt
or CoteVal
macro
use cote::prelude::*;
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(help, aborthelp)]
pub struct Cli {
#[arg(alias = "-s")]
speed: Speed,
#[arg(alias = "-d")]
direction: Direction,
#[pos()]
way: Way,
}
#[derive(Debug, PartialEq, Eq, CoteOpt)]
#[fetch(inner = i32, map = Speed)]
#[infer(val = i32)]
pub struct Speed(i32);
#[derive(Debug, PartialEq, Eq)]
pub enum Direction {
Up,
Down,
Left,
Right,
}
impl Infer for Direction {
type Val = Direction;
}
impl<S> Fetch<'_, S> for Direction
where
S: SetValueFindExt,
SetCfg<S>: ConfigValue + Default,
Self: ErasedTy + Sized,
{
}
impl Alter for Direction {}
impl RawValParser for Direction {
type Error = cote::Error;
fn parse(raw: Option<&RawVal>, ctx: &Ctx) -> cote::Result<Self> {
let name = raw2str(raw)?.to_lowercase();
let uid = ctx.uid()?;
match name.as_str() {
"up" => Ok(Direction::Up),
"down" => Ok(Direction::Down),
"left" => Ok(Direction::Left),
"right" => Ok(Direction::Right),
_ => Err(
raise_failure!("Unknow value for enum type `{0}`: {1}", "Direction", name)
.with_uid(uid),
),
}
}
}
#[derive(Debug, PartialEq, Eq, CoteVal, CoteOpt)]
#[coteval(igcase)]
pub enum Way {
Walk,
Bike,
Roll,
}
fn main() -> color_eyre::Result<()> {
color_eyre::install()?;
let cli = Cli::parse(Args::from(["app", "-s", "40", "-d=Left", "bike"]))?;
assert_eq!(cli.speed.0, 40);
assert_eq!(cli.direction, Direction::Left);
assert_eq!(cli.way, Way::Bike);
Ok(())
}
§Cote
Configurations list
§cote
name | need value | available value |
---|---|---|
policy | true | "pre" , "fwd" , "delay" , or type |
name | true | string literal |
help | false | |
helpopt | true | string literal |
head | true | string literal |
foot | true | string literal |
width | true | integer |
usagew | true | integer |
aborthelp | false | |
on | true | function or closure |
fallback | true | function or closure |
then | true | function or closure |
strict | true | boolean |
combine | false | |
embedded | false | |
flag | false |
policy
Configure the policy of current struct, its value should be fwd
, pre
or delay
.
The default value is fwd
if no sub command in the struct, otherwise it will be pre
.
use cote::prelude::*;
use std::any::Any;
// The default policy is `fwd`(FwdPolicy)
#[derive(Debug, Cote)]
pub struct Widget;
// Configure the policy with built-in `delay`, `pre` or `fwd`
#[derive(Debug, Cote)]
#[cote(policy = delay)]
pub struct Windows;
// Using `pre`(`PrePolicy`) if the struct has sub commands
#[derive(Debug, Cote)]
pub struct Button {
#[sub()]
#[allow(unused)]
wid: Option<Widget>,
#[sub()]
#[allow(unused)]
win: Option<Windows>,
}
// `NullPolicy` is a example policy can used in attribute `cote`
#[derive(Debug, Cote)]
#[cote(policy = NullPolicy)]
pub struct LineEditor;
#[test]
fn policy() {
assert!(policy_impl().is_ok());
}
fn policy_impl() -> Result<(), Box<dyn std::error::Error>> {
let widget = Widget::into_policy();
assert!(check_t::<FwdPolicy<'_, ASet, ASer>>(&widget));
let windows = Windows::into_policy();
assert!(check_t::<DelayPolicy<'_, ASet, ASer>>(&windows));
let button = Button::into_policy();
assert!(check_t::<PrePolicy<'_, ASet, ASer>>(&button));
let line_editor = LineEditor::into_policy();
assert!(check_t::<NullPolicy<'_, ASet, ASer>>(&line_editor));
Ok(())
}
fn check_t<T: 'static>(w: &dyn Any) -> bool {
w.is::<T>()
}
name
The name is display in usage information.
help
Add default help option -h
|--help
, generate help message when option set.
helpopt
Set help option generated by cote-derive
, default is "--help;-h=b: Display help message"
.
aborthelp
Display help message if any error raised or command line parsing failed.
head
,foot
Custom the help message display.
use cote::prelude::*;
#[derive(Debug, Cote)]
#[cote(
help,
head = "Set the head message here",
foot = "Set the foot message here"
)]
pub struct Cli;
#[test]
fn head_foot() {
assert!(head_foot_impl().is_ok());
}
fn head_foot_impl() -> Result<(), Box<dyn std::error::Error>> {
Cli::parse(Args::from(["app", "-h"].into_iter()))?;
// Output:
//
// Usage: cli [-h,--help]
//
// Set the head message here
//
// Options:
// -h,--help Display help message
//
// Set the foot message here
//
Ok(())
}
width
,usagew
width
set the maximum length of option help message. usagew
set the maximum count of options in usage.
See Configurating Help
.
on
,fallback
,then
Using then
you can configure a handler which is responsible for storing the option value
(which is generated from the struct and inserted by cote-derive).
In default the handler is process
,
and the action is App
or Set
.
And with on
, you can set a handler will be invoked by policy
,
the return value of handler will store as the value of option.
use cote::prelude::*;
use std::sync::OnceLock;
#[derive(Debug, Cote)]
#[cote(on = cli_main)]
pub struct Cli;
static FLAG: OnceLock<bool> = OnceLock::new();
fn cli_main<Set, Ser>(set: &mut Set, _: &mut Ser) -> cote::Result<Option<()>>
where
Set: cote::prelude::Set,
{
FLAG.get_or_init(|| true);
assert_eq!(set.len(), 1, "there is only one option here");
Ok(Some(()))
}
#[test]
fn on() {
assert!(on_impl().is_ok());
}
fn on_impl() -> color_eyre::Result<()> {
color_eyre::install()?;
Cli::parse(Args::from(["app"].into_iter()))?;
assert_eq!(FLAG.get(), Some(&true), "Set flag in cli_main");
Ok(())
}
The fallback
do same things as on
except the fallback
will be called
if the handler returns None
.
use cote::prelude::*;
use std::sync::OnceLock;
#[derive(Debug, Cote)]
#[cote(fallback = cli_main, then = storer)]
pub struct Cli;
static FLAG: OnceLock<bool> = OnceLock::new();
fn cli_main<Set, Ser>(_: &mut Set, _: &mut Ser) -> cote::Result<Option<()>> {
FLAG.get_or_init(|| true);
Ok(None)
}
fn storer<Set, Ser>(
_: Uid,
_: &mut Set,
_: &mut Ser,
_: Option<&RawVal>,
_: Option<()>,
) -> cote::Result<bool> {
unreachable!("not call here if cli_main returns None")
}
#[test]
fn fallback() {
assert!(fallback_impl().is_ok());
}
fn fallback_impl() -> color_eyre::Result<()> {
color_eyre::install()?;
let CoteRes { parser, .. } = Cli::parse_args(Args::from(["app"].into_iter()))?;
assert_eq!(FLAG.get(), Some(&true), "Set flag in cli_main");
assert_eq!(
parser.find_opt("".infer::<Main>())?.rawval()?,
&RawVal::from("app")
);
Ok(())
}
strict
Enable the strict mode of parser by calling the set_strict
.
use cote::prelude::*;
#[derive(Debug, Cote)]
#[cote(help, strict = true)]
pub struct Cli;
#[test]
fn strict() {
assert!(strict_impl().is_ok());
}
fn strict_impl() -> color_eyre::Result<()> {
color_eyre::install()?;
let ret = Cli::parse(Args::from(["app", "--opt-a"].into_iter()));
assert!(ret.is_err());
if let Some(err) = ret.err() {
assert_eq!(
err.to_string(),
"Parsing arguments `\"--opt-a\"` failed: None"
);
}
Ok(())
}
combine
,embedded
,flag
Enable some extra user style
of policy. See also Configurating User Style
.
§arg
, pos
, cmd
name | need value | available value |
---|---|---|
name | true | string literal |
ty | true | type |
hint | true | string literal |
help | true | string literal |
value | true | value expression |
values | true | values expression |
alias | true | string literal |
index | true | range or integer |
force | true | boolean |
action | true | Action |
valid | true | valid! |
on | true | function or closure |
fallback | true | function or closure |
then | true | function or closure |
nodelay | false | |
fetch | true | function |
append | false | |
count | false |
name
,alias
Configure the name and alias of current option. See also Configurating the name and alias
.
hint
,help
Configure the name and help message of option.
See also Configurating the hint, help and default value
.
value
,values
Configure the default value of option, cote-derive
using From
convert given value to option value.
use cote::prelude::*;
#[derive(Debug, Cote)]
#[cote()]
pub struct Cli {
#[arg(value = "tools")]
name: String,
#[pos(index = 1.., values = ["a", "b"])]
args: Vec<String>,
}
#[test]
fn value() {
assert!(value_impl().is_ok());
}
fn value_impl() -> color_eyre::Result<()> {
color_eyre::install()?;
let cli = Cli::parse(Args::from(["app", "c"].into_iter()))?;
assert_eq!(cli.name.as_str(), "tools");
assert_eq!(
cli.args,
vec!["a".to_owned(), "b".to_owned(), "c".to_owned()]
);
Ok(())
}
index
Configure the index of option, it is using for pos
(Pos
) attribute generally.
use cote::prelude::*;
#[derive(Debug, Cote)]
#[cote()]
pub struct Cli {
#[pos()]
name: String,
#[pos(index = 2..)]
args: Vec<u64>,
}
#[test]
fn index() {
assert!(index_impl().is_ok());
}
fn index_impl() -> color_eyre::Result<()> {
color_eyre::install()?;
let cli = Cli::parse(Args::from(["app", "index", "2", "3", "4"].into_iter()))?;
assert_eq!(cli.name.as_str(), "index");
assert_eq!(cli.args, vec![2, 3, 4]);
Ok(())
}
force
Make the option force required.
use cote::prelude::*;
#[derive(Debug, Cote)]
#[cote()]
pub struct Cli {
#[allow(unused)]
#[pos(force = true)]
name: Option<String>,
}
#[test]
fn force() {
assert!(force_impl().is_ok());
}
fn force_impl() -> color_eyre::Result<()> {
color_eyre::install()?;
// name is not set, parse failed
assert!(Cli::parse(Args::from(["app"].into_iter())).is_err());
Ok(())
}
action
,ty
,append
,count
action
can configure the Action
which responsible for saving value of option.
Using ty
specify the option type when using Action::Cnt
.
use cote::prelude::*;
#[derive(Debug, Cote)]
#[cote()]
pub struct Cli {
// `count` is an alias of `action = cote::Action::Cnt`
#[arg(alias = "-v", ty = bool, count)]
verbose: u64,
}
#[test]
fn action() {
assert!(action_impl().is_ok());
}
fn action_impl() -> color_eyre::Result<()> {
color_eyre::install()?;
let cli = Cli::parse(Args::from(["app", "-v", "-v", "-v"].into_iter()))?;
assert_eq!(cli.verbose, 3);
Ok(())
}
append
is an alias of “action = Action::App
”,
count
is an alias of “action = Action::Cnt
”
fetch
Configure the handler which is used to extract value from set
.
use cote::prelude::*;
use std::time::{Duration, SystemTime};
#[derive(Debug, Cote)]
#[cote()]
pub struct Cli {
// Using `index` and `append` collect the position arguments 1..
#[pos(index = 1.., append, fetch = random_select)]
name: String,
}
#[test]
fn fetch() {
assert!(fetch_impl().is_ok());
}
fn fetch_impl() -> color_eyre::Result<()> {
color_eyre::install()?;
let names = ["lily", "lucy", "bob", "joe"];
let cli = Cli::parse(Args::from(["app"].into_iter().chain(names.into_iter())))?;
assert!(names.contains(&cli.name.as_str()));
Ok(())
}
// The fetch handler which is used to extract value from `set`
fn random_select<T, S>(uid: Uid, set: &mut S) -> cote::Result<T>
where
T: ErasedTy + Default,
S: Set + SetValueFindExt,
SetCfg<S>: ConfigValue + Default,
{
let vals = set.opt_mut(uid)?.vals_mut::<T>()?;
let len = vals.len();
let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH);
let now = now.unwrap_or(Duration::from_secs(1));
Ok(std::mem::take(&mut vals[now.as_secs() as usize % len]))
}
valid
Using valid!
validate the value set by user. See also Validate values
.
use cote::prelude::*;
#[derive(Debug, Cote)]
#[cote()]
pub struct Cli {
#[allow(unused)]
#[pos(valid = valid!(["lily", "lucy", "bob", "joe"]))]
name: String,
}
#[test]
fn valid() {
assert!(valid_impl().is_ok());
}
fn valid_impl() -> color_eyre::Result<()> {
color_eyre::install()?;
assert!(Cli::parse(Args::from(["app", "lily"].into_iter())).is_ok());
assert!(Cli::parse(Args::from(["app", "jim"].into_iter())).is_err());
Ok(())
}
on
,fallback
,then
Using then
you can configure a handler which is responsible for storing the option value.
In default the handler is process
, and the action is Null
.
And with on
, you can set a handler will be invoked by policy
,
the return value of handler will store as the value of option.
use std::ops::Add;
use cote::prelude::*;
#[derive(Debug, Cote)]
#[cote()]
pub struct Cli {
#[arg(on = plus_one, then = plus_two)]
value: i64,
}
#[test]
fn on() {
assert!(on_impl().is_ok());
}
fn on_impl() -> color_eyre::Result<()> {
color_eyre::install()?;
let cli = Cli::parse(Args::from(["app", "--value=39"].into_iter()))?;
assert_eq!(cli.value, 42);
Ok(())
}
fn plus_one<Set, Ser>(
_: &mut Set,
_: &mut Ser,
val: ctx::Value<i64>, // extract value from argument
) -> cote::Result<Option<i64>> {
Ok(Some(val.add(1)))
}
fn plus_two<Set, Ser>(
uid: Uid,
set: &mut Set,
ser: &mut Ser,
raw: Option<&RawVal>,
val: Option<i64>,
) -> cote::Result<bool>
where
Set: SetValueFindExt,
SetCfg<Set>: ConfigValue + Default,
{
let mut act = *set.opt_mut(uid)?.action();
act.process(uid, set, ser, raw, val.map(|v| v + 2))
}
The fallback
do same things as on
except the fallback
will be called
if the handler returns None
.
nodelay
Invoke the option’s handler before any Cmd
or Pos
.
Only work for DelayPolicy
currently.
See also Add "no delay" option
.
§sub
name | need value | available value |
---|---|---|
policy | true | "pre" , "fwd" , "delay" , or type |
name | true | string literal |
hint | true | string literal |
help | true | string literal |
head | true | string literal |
foot | true | string literal |
alias | true | string literal |
force | true | boolean |
policy
Override the policy
of sub command.
use std::sync::Mutex;
use cote::prelude::*;
static GLOBAL_CNT: Mutex<i32> = Mutex::new(0);
macro_rules! order {
($n:literal, $t:ident) => {
|_: &mut Parser<'_, Set, Ser>, _: &mut Ser, mut val: ctx::Value<$t>| {
*GLOBAL_CNT.lock().unwrap() += 1;
assert_eq!($n, *GLOBAL_CNT.lock().unwrap());
Ok(Some(val.take()))
}
};
}
#[derive(Debug, Cote)]
#[cote(help)]
pub struct Cli {
#[allow(unused)]
debug: bool,
#[allow(unused)]
#[sub(policy = delay)]
query: Option<Query>,
}
#[derive(Debug, Cote)]
pub struct Query {
#[allow(unused)]
#[arg(nodelay, on = order!(1, usize))]
row: usize, // `nodelay` option will be process immediately,
// before `col` and `format`
#[allow(unused)]
#[arg(on = order!(3, usize))]
col: usize, // `col` is process after `format` when using `DelayPolicy`
#[allow(unused)]
#[pos(on = order!(2, String))]
format: String,
}
#[test]
fn policy() {
assert!(policy_impl().is_ok());
}
fn policy_impl() -> color_eyre::Result<()> {
color_eyre::install()?;
Cli::parse(Args::from(
["app", "query", "--col=1", "--row=2", "Query msg: {:?}"].into_iter(),
))?;
assert_eq!(*GLOBAL_CNT.lock().unwrap(), 3);
Ok(())
}
name
,alias
Configure the name and alias of sub command.
hint
,help
Configure the hint and help of help message.
head
,foot
Configure the head and foot of help message of sub command.
use cote::prelude::*;
#[derive(Debug, Cote)]
#[cote(help, aborthelp)]
pub struct Cli {
#[allow(unused)]
debug: bool,
/// Query sub command
#[allow(unused)]
#[sub(foot = "Foot message of sub command query")]
#[sub(name = "q", head = "Head message of sub command query")]
query: Option<Query>,
}
#[derive(Debug, Cote)]
#[cote(help)]
pub struct Query {
#[allow(unused)]
#[arg(hint = "--row <usize>", help = "Set the row data of query")]
row: usize,
/// Set the format of query output
#[allow(unused)]
#[pos()]
format: String,
}
#[test]
fn help() {
assert!(help_impl().is_ok());
}
fn help_impl() -> color_eyre::Result<()> {
color_eyre::install()?;
Cli::parse(Args::from(["app", "q", "--help"]))?;
// Output:
// Usage: cli q [-h,--help] <--row <usize>> [ARGS]
//
// Head message of sub command query
//
// Options:
// -h,--help Display help message
// --row <usize> Set the row data of query
//
// Args:
// format@1 Set the format of query output
//
// Foot message of sub command
Ok(())
}
force
Configure the sub command optional, in default one of sub commands must be set.
use cote::prelude::*;
#[derive(Debug, Cote)]
#[cote(help, aborthelp)]
pub struct Cli {
#[allow(unused)]
debug: bool,
#[sub(force = false)]
query: Option<Query>,
}
#[derive(Debug, Cote, PartialEq, Eq)]
#[cote(help)]
pub struct Query {
#[allow(unused)]
row: usize,
#[allow(unused)]
#[pos()]
format: String,
}
#[test]
fn force() {
assert!(force_impl().is_ok());
}
fn force_impl() -> color_eyre::Result<()> {
color_eyre::install()?;
assert_eq!(Cli::parse(Args::from(["app"].into_iter()))?.query, None);
Ok(())
}
§CoteOpt
Configurations list
CoteOpt
derive the default behavior of Infer
, Fetch
and Alter
.
§infer
name | need value | available value |
---|---|---|
val | true | value type |
action | true | Action |
force | true | boolean |
ctor | true | AStr |
index | true | Option<Index > |
style | true | Vec<Style > |
igname | true | boolean |
igalias | true | boolean |
igindex | true | boolean |
valid | true | Option<ValValidator <Val >> |
init | true | Option<ValInitializer > |
ty | true | TypeId |
tweak | true | function |
fill | true | function |
infer
can configure the behavior of Infer
, the configures are mostly using to providing default value.
§Example
use aopt::value::AnyValue;
use cote::prelude::*;
use std::{any::TypeId, fmt::Debug};
#[derive(Debug, PartialEq, Eq, CoteOpt)]
#[infer(val = i32, action = Action::App)]
#[infer(init = Some(ValInitializer::new_value(42i32)))]
pub struct Speed(i32);
#[test]
fn infer() {
assert!(infer_impl().is_ok());
}
fn infer_impl() -> color_eyre::Result<()> {
color_eyre::install()?;
assert_eq!(
TypeId::of::<<Speed as Infer>::Val>(),
TypeId::of::<i32>(),
"same type"
);
assert_eq!(<Speed as Infer>::infer_act(), Action::App);
assert!(!<Speed as Infer>::infer_force());
assert_eq!(<Speed as Infer>::infer_ctor(), ctor_default_name());
assert_eq!(<Speed as Infer>::infer_index(), None);
assert_eq!(<Speed as Infer>::infer_style(), vec![Style::Argument]);
assert!(!<Speed as Infer>::infer_ignore_name());
assert!(<Speed as Infer>::infer_ignore_index());
assert!(!<Speed as Infer>::infer_ignore_alias());
assert!(<Speed as Infer>::infer_validator().is_none());
assert_eq!(<Speed as Infer>::infer_type_id(), TypeId::of::<i32>());
check_initializer(<Speed as Infer>::infer_initializer(), 42)?;
Ok(())
}
fn check_initializer<T: PartialEq + Debug + ErasedTy>(
init: Option<ValInitializer>,
val: T,
) -> color_eyre::Result<()> {
assert!(init.is_some());
if let Some(mut init) = init {
let mut any_value = AnyValue::new();
// put the value into AnyValue
init.invoke(&mut any_value)?;
assert_eq!(any_value.val::<T>()?, &val);
}
Ok(())
}
§alter
alter
is reserve for future using.
§fetch
name | need value | available value |
---|---|---|
inner | true | type |
map | true | function |
scalar | true | function |
vector | true | function |
fetch
can configure the behavior of Fetch
.
You can use inner
and map
configure the type and map function.
Or use scalar
or vector
configure the fetch_uid
and fetch_vec_uid
separately.
§Example
use aopt::prelude::AFwdParser;
use cote::prelude::*;
#[derive(Debug, PartialEq, Eq, CoteOpt)]
#[infer(val = i32)]
#[fetch(inner = i32, map = Speed)]
pub struct Speed(i32);
#[test]
fn fetch() {
assert!(fetch_impl().is_ok());
}
fn fetch_impl() -> color_eyre::Result<()> {
color_eyre::install()?;
let mut parser = AFwdParser::default();
parser.add_opt("--speed".infer::<i32>())?;
parser.parse(ARef::new(Args::from(["app", "--speed=42"].into_iter())))?;
assert_eq!(Speed::fetch("--speed", parser.optset_mut())?, Speed(42));
Ok(())
}
§CoteVal
Configurations list
CoteVal
derive the default behavior of RawValParser
.
§coteval
name | need value | available value |
---|---|---|
forward | true | type |
map | true | function |
mapraw | true | function |
mapstr | true | function |
igcase | false | |
name | true | string literal |
alias | true | string literal |
coteval
can configure the behavior of RawValParser
.
Using forward
and map
you can forward the call to another type, and then map the value to current type.
Or you can use mapraw
, mapstr
pass a parser called by parse
.
CoteVal
also support generate default parsing code for simple enum type.
For enum type, you can use igcase
ignore case when matching, name
configure the name of matching
or use alias
add other names of matching.
§Example 1
use aopt::prelude::AFwdParser;
use cote::prelude::*;
#[derive(Debug, PartialEq, Eq, CoteOpt, CoteVal)]
#[coteval(forward = i32, map = Speed)]
pub struct Speed(i32);
#[derive(Debug, CoteVal, CoteOpt, PartialEq)]
#[coteval(igcase)]
pub enum IM {
#[coteval(alias = "qq")]
OICQ,
ICQ,
Line,
Skype,
WeChat,
}
#[test]
fn value() {
assert!(value_impl().is_ok());
}
fn value_impl() -> color_eyre::Result<()> {
color_eyre::install()?;
let mut parser = AFwdParser::default();
parser.add_opt("--speed".infer::<Speed>())?;
parser.add_opt("-im;--instant-message".infer::<IM>())?;
parser.parse(ARef::new(Args::from(
["app", "--speed=42", "-im=qq"].into_iter(),
)))?;
assert_eq!(Speed::fetch("--speed", parser.optset_mut())?, Speed(42));
assert_eq!(
IM::fetch("--instant-message", parser.optset_mut())?,
IM::OICQ
);
Ok(())
}
§Example of mapraw
and mapstr
use aopt::prelude::AFwdParser;
use cote::prelude::*;
use regex::Regex;
#[derive(Debug, CoteVal, CoteOpt, PartialEq)]
#[coteval(mapstr = Meal::new)]
pub enum Meal {
BreakFast,
Lunch,
Dinner,
}
impl Meal {
pub fn new(value: &str) -> cote::Result<Self> {
match value {
"breakfast" => Ok(Self::BreakFast),
"lunch" => Ok(Self::Lunch),
"dinner" => Ok(Self::Dinner),
name => {
panic!("Unknow {name} for Meal")
}
}
}
}
#[derive(Debug, PartialEq, Eq, CoteOpt, CoteVal)]
#[coteval(mapraw = Point::new)]
pub struct Point {
x: i32,
y: i32,
}
impl Point {
pub fn new(raw: Option<&RawVal>, _: &Ctx) -> cote::Result<Self> {
let regex = Regex::new(r"[\{\[\(]\s*(\d+)\s*\,\s*(\d+)\s*[\}\]\)]").unwrap();
if let Some(captures) = regex.captures(raw2str(raw)?) {
let mut x = 0;
let mut y = 0;
if let Some(mat) = captures.get(1) {
x = mat.as_str().parse::<i32>().map_err(|_| {
raise_error!("Point.x must be a valid number: `{}`", mat.as_str())
})?;
}
if let Some(mat) = captures.get(2) {
y = mat.as_str().parse::<i32>().map_err(|_| {
raise_error!("Point.y must be a valid number: `{}`", mat.as_str())
})?;
}
return Ok(Point { x, y });
}
panic!("Can not parsing value of Point")
}
}
#[test]
fn map() {
assert!(map_impl().is_ok());
}
fn map_impl() -> color_eyre::Result<()> {
color_eyre::install()?;
let mut parser = AFwdParser::default();
parser.add_opt("-p;--point".infer::<Point>())?;
parser.add_opt("--meal".infer::<Meal>())?;
parser.parse(ARef::new(Args::from(
["app", "-p={42,2}", "--meal=lunch"].into_iter(),
)))?;
assert_eq!(
Point::fetch("--point", parser.optset_mut())?,
Point { x: 42, y: 2 }
);
assert_eq!(Meal::fetch("--meal", parser.optset_mut())?, Meal::Lunch);
Ok(())
}