[][src]Macro argwerk::parse

macro_rules! parse {
    (
        $it:expr => $($rest:tt)*
    ) => { ... };
    (
        $($rest:tt)*
    ) => { ... };
}

Parse commandline arguments.

This will generate an anonymous structure containing the arguments defined which is returned by the macro.

Each branch is executed when an incoming argument matches and must return a Result, like Ok(()). Error raised in the branch will cause a ErrorKind::Error error to be raised associated with that argument with the relevant error attached.

This also generated two helper functions available in the parse branches:

The parse macro can be invoked in two ways.

Using std::env::args() to get arguments from the environment:

let args = argwerk::parse! {
    /// A simple test command.
    "command [-h]" {
        help: bool,
        limit: usize = 10,
    }
}?;

Or explicitly specifying an iterator to use with <iter> => <config>. This works with anything that implements AsRef<str>:

let args = argwerk::parse! {
    vec!["foo", "bar", "baz"] =>
    /// A simple test command.
    "command [-h]" {
        help: bool,
        limit: usize = 10,
        positional: Option<(&'static str, &'static str, &'static str)>,
    }
    (a, b, c) => {
        positional = Some((a, b, c));
        Ok(())
    }
}?;

assert_eq!(args.positional, Some(("foo", "bar", "baz")));

Arguments structure

The first part of the parse macro defines the state available to the parser. These are field-like declarations which can specify a default value. This is the only required component of the macro.

Fields which do not specify an initialization value will be initialized through Default::default.

let args = argwerk::parse! {
    /// A simple test command.
    "command [-h]" {
        help: bool,
        limit: usize = 10,
    }
}?;

assert_eq!(args.help, false);
assert_eq!(args.limit, 10);

Argument branches

The basic form of an argument branch is:

let args = argwerk::parse! {
    vec![String::from("-h")] =>
    /// A simple test command.
    "command [-h]" {
        help: bool,
    }
    /// Print the help.
    "-h" | "--help" => {
        help = true;
        print_help();
        Ok(())
    }
}?;

assert_eq!(args.help, true);

Documentation

You specify documentation for argument branches with doc comments. These are automatically wrapped to 80 characters and pretty printed.

let args = argwerk::parse! {
    vec![String::from("-h")] =>
    /// A simple test command.
    "command [-h]" {
        help: bool,
    }
    /// Prints the help.
    ///
    /// This includes:
    ///    * All the available switches.
    ///    * All the available positional arguments.
    ///    * Whatever else the developer decided to put in here! We even support wrapping comments which are overly long.
    "-h" | "--help" => {
        help = true;
        print_help();
        Ok(())
    }
}?;

This would print:

Usage: command [-h]
A simple test command.

This is nice!

Options:
  -h, --help  Prints the help.

              This includes:
                 * All the available switches.
                 * All the available positional arguments.
                 * Whatever else the developer decided to put in here! We even
                   support wrapping comments which are overly long.

We determine the initial indentation level from the first doc comment. Above this would be "Prints the help.".

When we wrap individual lines, we determine the indentation level to use by finding the first alphanumerical character on the previous line. This is why the "overly long comment" above wraps correctly in the markdown list.

Parse all available arguments with #[rest]

You can write a branch that receives the rest of the arguments using the #[rest] attribute.

let args = argwerk::parse! {
    vec![String::from("foo"), String::from("bar"), String::from("baz")] =>
    /// A simple test command.
    "command [-h]" {
        rest: Vec<String>,
    }
    #[rest] args => {
        rest = args;
        Ok(())
    }
}?;

assert_eq!(args.rest, &["foo", "bar", "baz"]);

Parse optional arguments with #[option]

Switches and positional arguments can be marked with the #[option] attribute. This will cause the argument to take a value of type Option<I::Item>.

An optional argument parses to None if:

  • There are no more arguments to parse.
  • The argument is a switch (starts with -).
let parser = |iter: &[&str]| argwerk::parse! {
    iter.iter().copied().map(String::from) =>
    /// A simple test command.
    "command [-h]" {
        foo: Option<String>,
        bar: bool,
    }
    /// A switch taking an optional argument.
    "--foo", #[option] arg => {
        foo = arg;
        Ok(())
    }
    "--bar" => {
        bar = true;
        Ok(())
    }
};

// Argument exists, but looks like a switch.
let args = parser(&["--foo", "--bar"])?;
assert_eq!(args.foo.as_deref(), None);
assert!(args.bar);

// Argument does not exist.
let args = parser(&["--foo"])?;
assert_eq!(args.foo.as_deref(), None);
assert!(!args.bar);

let args = parser(&["--foo", "bar"])?;
assert_eq!(args.foo.as_deref(), Some("bar"));
assert!(!args.bar);

Parse positional arguments

Positional arguments are parsed by specifying a tuple in the match branch.

Positional arguments support the following attributes:

  • #[option] - which will cause the argument to be optionally parsed into an Option<I::Item>.
  • #[rest] - which will parse the rest of the arguments.

The following is a basic example without attributes. Both foo and bar are required if the branch matches.

let args = argwerk::parse! {
    ["a", "b"].iter().copied().map(String::from) =>
    /// A simple test command.
    "command [-h]" {
        positional: Option<(String, String)>,
    }
    /// Takes argument at <foo> and <bar>.
    (foo, bar) if positional.is_none() => {
        positional = Some((foo, bar));
        Ok(())
    }
}?;

assert_eq!(args.positional, Some((String::from("a"), String::from("b"))));

The following showcases positional parameters using #[option] and #[rest].

let args = argwerk::parse! {
    ["foo", "bar", "baz"].iter().copied().map(String::from) =>
    /// A simple test command.
    "command [-h]" {
        first: String,
        second: Option<String>,
        rest: Vec<String>,
    }
    (a, #[option] b, #[rest] c) => {
        first = a;
        second = b;
        rest = c;
        Ok(())
    }
}?;

assert_eq!(args.first, "foo");
assert_eq!(args.second.as_deref(), Some("bar"));
assert_eq!(args.rest, &["baz"]);