Crate argh

source ·
Expand description

Derive-based argument parsing optimized for code size and conformance to the Fuchsia commandline tools specification

The public API of this library consists primarily of the FromArgs derive and the from_env function, which can be used to produce a top-level FromArgs type from the current program’s commandline arguments.

Basic Example

use argh::FromArgs;

#[derive(FromArgs)]
/// Reach new heights.
struct GoUp {
    /// whether or not to jump
    #[argh(switch, short = 'j')]
    jump: bool,

    /// how high to go
    #[argh(option)]
    height: usize,

    /// an optional nickname for the pilot
    #[argh(option)]
    pilot_nickname: Option<String>,
}

let up: GoUp = argh::from_env();

./some_bin --help will then output the following:

Usage: cmdname [-j] --height <height> [--pilot-nickname <pilot-nickname>]

Reach new heights.

Options:
  -j, --jump        whether or not to jump
  --height          how high to go
  --pilot-nickname  an optional nickname for the pilot
  --help            display usage information

The resulting program can then be used in any of these ways:

  • ./some_bin --height 5
  • ./some_bin -j --height 5
  • ./some_bin --jump --height 5 --pilot-nickname Wes

Switches, like jump, are optional and will be set to true if provided.

Options, like height and pilot_nickname, can be either required, optional, or repeating, depending on whether they are contained in an Option or a Vec. Default values can be provided using the #[argh(default = "<your_code_here>")] attribute, and in this case an option is treated as optional.

use argh::FromArgs;

fn default_height() -> usize {
    5
}

#[derive(FromArgs)]
/// Reach new heights.
struct GoUp {
    /// an optional nickname for the pilot
    #[argh(option)]
    pilot_nickname: Option<String>,

    /// an optional height
    #[argh(option, default = "default_height()")]
    height: usize,

    /// an optional direction which is "up" by default
    #[argh(option, default = "String::from(\"only up\")")]
    direction: String,
}

fn main() {
    let up: GoUp = argh::from_env();
}

Custom option types can be deserialized so long as they implement the FromArgValue trait (automatically implemented for all FromStr types). If more customized parsing is required, you can supply a custom fn(&str) -> Result<T, String> using the from_str_fn attribute:


#[derive(FromArgs)]
/// Goofy thing.
struct FiveStruct {
    /// always five
    #[argh(option, from_str_fn(always_five))]
    five: usize,
}

fn always_five(_value: &str) -> Result<usize, String> {
    Ok(5)
}

Positional arguments can be declared using #[argh(positional)]. These arguments will be parsed in order of their declaration in the structure:

use argh::FromArgs;
#[derive(FromArgs, PartialEq, Debug)]
/// A command with positional arguments.
struct WithPositional {
    #[argh(positional)]
    first: String,
}

The last positional argument may include a default, or be wrapped in Option or Vec to indicate an optional or repeating positional argument.

If your final positional argument has the greedy option on it, it will consume any arguments after it as if a -- were placed before the first argument to match the greedy positional:

use argh::FromArgs;
#[derive(FromArgs, PartialEq, Debug)]
/// A command with a greedy positional argument at the end.
struct WithGreedyPositional {
    /// some stuff
    #[argh(option)]
    stuff: Option<String>,
    #[argh(positional, greedy)]
    all_the_rest: Vec<String>,
}

Now if you pass --stuff Something after a positional argument, it will be consumed by all_the_rest instead of setting the stuff field.

Note that all_the_rest won’t be listed as a positional argument in the long text part of help output (and it will be listed at the end of the usage line as [all_the_rest...]), and it’s up to the caller to append any extra help output for the meaning of the captured arguments. This is to enable situations where some amount of argument processing needs to happen before the rest of the arguments can be interpreted, and shouldn’t be used for regular use as it might be confusing.

Subcommands are also supported. To use a subcommand, declare a separate FromArgs type for each subcommand as well as an enum that cases over each command:


#[derive(FromArgs, PartialEq, Debug)]
/// Top-level command.
struct TopLevel {
    #[argh(subcommand)]
    nested: MySubCommandEnum,
}

#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand)]
enum MySubCommandEnum {
    One(SubCommandOne),
    Two(SubCommandTwo),
}

#[derive(FromArgs, PartialEq, Debug)]
/// First subcommand.
#[argh(subcommand, name = "one")]
struct SubCommandOne {
    #[argh(option)]
    /// how many x
    x: usize,
}

#[derive(FromArgs, PartialEq, Debug)]
/// Second subcommand.
#[argh(subcommand, name = "two")]
struct SubCommandTwo {
    #[argh(switch)]
    /// whether to fooey
    fooey: bool,
}

You can also discover subcommands dynamically at runtime. To do this, declare subcommands as usual and add a variant to the enum with the dynamic attribute. Instead of deriving FromArgs, the value inside the dynamic variant should implement DynamicSubCommand.


#[derive(FromArgs, PartialEq, Debug)]
/// Top-level command.
struct TopLevel {
    #[argh(subcommand)]
    nested: MySubCommandEnum,
}

#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand)]
enum MySubCommandEnum {
    Normal(NormalSubCommand),
    #[argh(dynamic)]
    Dynamic(Dynamic),
}

#[derive(FromArgs, PartialEq, Debug)]
/// Normal subcommand.
#[argh(subcommand, name = "normal")]
struct NormalSubCommand {
    #[argh(option)]
    /// how many x
    x: usize,
}

/// Dynamic subcommand.
#[derive(PartialEq, Debug)]
struct Dynamic {
    name: String
}

impl DynamicSubCommand for Dynamic {
    fn commands() -> &'static [&'static CommandInfo] {
        static RET: OnceCell<Vec<&'static CommandInfo>> = OnceCell::new();
        RET.get_or_init(|| {
            let mut commands = Vec::new();

            // argh needs the `CommandInfo` structs we generate to be valid
            // for the static lifetime. We can allocate the structures on
            // the heap with `Box::new` and use `Box::leak` to get a static
            // reference to them. We could also just use a constant
            // reference, but only because this is a synthetic example; the
            // point of using dynamic commands is to have commands you
            // don't know about until runtime!
            commands.push(&*Box::leak(Box::new(CommandInfo {
                name: "dynamic_command",
                description: "A dynamic command",
            })));

            commands
        })
    }

    fn try_redact_arg_values(
        command_name: &[&str],
        args: &[&str],
    ) -> Option<Result<Vec<String>, EarlyExit>> {
        for command in Self::commands() {
            if command_name.last() == Some(&command.name) {
                // Process arguments and redact values here.
                if !args.is_empty() {
                    return Some(Err("Our example dynamic command never takes arguments!"
                                    .to_string().into()));
                }
                return Some(Ok(Vec::new()))
            }
        }
        None
    }

    fn try_from_args(command_name: &[&str], args: &[&str]) -> Option<Result<Self, EarlyExit>> {
        for command in Self::commands() {
            if command_name.last() == Some(&command.name) {
                if !args.is_empty() {
                    return Some(Err("Our example dynamic command never takes arguments!"
                                    .to_string().into()));
                }
                return Some(Ok(Dynamic { name: command.name.to_string() }))
            }
        }
        None
    }
}

Programs that are run from an environment such as cargo may find it useful to have positional arguments present in the structure but omitted from the usage output. This can be accomplished by adding the hidden_help attribute to that argument:


#[derive(FromArgs)]
/// Cargo arguments
struct CargoArgs {
    // Cargo puts the command name invoked into the first argument,
    // so we don't want this argument to show up in the usage text.
    #[argh(positional, hidden_help)]
    command: String,
    /// an option used for internal debugging
    #[argh(option, hidden_help)]
    internal_debugging: String,
    #[argh(positional)]
    real_first_arg: String,
}

Structs

  • Information to display to the user about why a FromArgs construction exited early.
  • Information about a documented error code.
  • Information about a flag or option.
  • Information about positional arguments

Enums

  • The kind of flags.
  • The optionality defines the requirments related to the presence of the argument on the command line.

Traits

  • Structured information about the command line arguments.
  • Trait implemented by values returned from a dynamic subcommand handler.
  • A type which can be the receiver of a Flag.
  • Types which can be constructed from a single commandline value.
  • Types which can be constructed from a set of commandline arguments.
  • A FromArgs implementation that represents a single subcommand.
  • A FromArgs implementation that can parse into one or more subcommands.
  • A top-level FromArgs implementation that is not a subcommand.

Functions

  • Create a FromArgs type from the current process’s env::args.
  • Create a FromArgs type from the current process’s env::args.

Type Definitions

Derive Macros

  • Entrypoint for #[derive(ArgsInfo)].
  • Entrypoint for #[derive(FromArgs)].