Module cote::_reference

source ·
Expand description

§Documentation: Cote Tutorial

  1. Quick Start
    1. Help message generate
    2. Running
  2. Configurating Struct
    1. Configurating Policy
    2. Configurating Help
    3. Configurating User Style
  3. Configurating Field
    1. Options
    2. Positionals
    3. Command Flags
    4. Sub Commands
  4. Configurating Options, Command flags and Positionals
    1. Configurating the name and alias
    2. Configurating the hint, help and default value
    3. Configurating the index
    4. Force required Positionals and Options
    5. Configurating action
    6. Configurating handler
    7. Validate values
    8. Add “no delay” option
  5. Configurating Sub Commands
    1. Configurating Policy
    2. Configurating name and alias
    3. Configurating help message
    4. Optional Sub commands
  6. How it works
    1. Traits
    2. Cote Configurations list
    3. CoteOpt Configurations list
    4. CoteVal Configurations list

§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: fwdpre 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

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(())
}

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 for Flag.

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 or pos

    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.

typeactionforce requiredforce required if has default value
TAction::Settruefalse
Option<T>Action::Setfalsefalse
Vec<T>Action::Apptruefalse
Option<Vec<T>>Action::Appfalsefalse
Pos<T>Action::Settruefalse
boolAction::Setfalsefalse
CmdAction::Settruetrue

§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
nameneed valueavailable value
policytrue"pre", "fwd", "delay", or type
nametruestring literal
helpfalse
helpopttruestring literal
headtruestring literal
foottruestring literal
widthtrueinteger
usagewtrueinteger
aborthelpfalse
ontruefunction or closure
fallbacktruefunction or closure
thentruefunction or closure
stricttrueboolean
combinefalse
embeddedfalse
flagfalse
  • 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
nameneed valueavailable value
nametruestring literal
tytruetype
hinttruestring literal
helptruestring literal
valuetruevalue expression
valuestruevalues expression
aliastruestring literal
indextruerange or integer
forcetrueboolean
actiontrueAction
validtruevalid!
ontruefunction or closure
fallbacktruefunction or closure
thentruefunction or closure
nodelayfalse
fetchtruefunction
appendfalse
countfalse
  • 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
nameneed valueavailable value
policytrue"pre", "fwd", "delay", or type
nametruestring literal
hinttruestring literal
helptruestring literal
headtruestring literal
foottruestring literal
aliastruestring literal
forcetrueboolean
  • 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
nameneed valueavailable value
valtruevalue type
actiontrueAction
forcetrueboolean
ctortrueAStr
indextrueOption<Index>
styletrueVec<Style>
ignametrueboolean
igaliastrueboolean
igindextrueboolean
validtrueOption<ValValidator<Val>>
inittrueOption<ValInitializer>
tytrueTypeId
tweaktruefunction
filltruefunction

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
nameneed valueavailable value
innertruetype
maptruefunction
scalartruefunction
vectortruefunction

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
nameneed valueavailable value
forwardtruetype
maptruefunction
maprawtruefunction
mapstrtruefunction
igcasefalse
nametruestring literal
aliastruestring 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(())
}