Crate argp

Source
Expand description

Derive-based argument parsing optimized for code size and flexibility.

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

§Basic Example

use argp::FromArgs;

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

    /// How high to go.
    #[argp(option)]
    height: usize,

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

let up: GoUp = argp::parse_args_or_exit(argp::DEFAULT);

./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            Show this help message and exit.

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 #[argp(default = "<your_code_here>")] attribute, and in this case an option is treated as optional.

use argp::FromArgs;

fn default_height() -> usize {
    5
}

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

    /// An optional height.
    #[argp(option, default = "default_height()")]
    height: usize,

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

fn main() {
    let up: GoUp = argp::parse_args_or_exit(argp::DEFAULT);
}

Custom option types can be deserialized so long as they implement the FromArgValue trait (already implemented for most types in std for which the FromStr trait is implemented). If more customized parsing is required, you can supply a custom fn(&str) -> Result<T, E> using the from_str_fn attribute, or fn(&OsStr) -> Result<T, E> using the from_os_str_fn attribute, where E implements ToString:


/// Goofy thing.
#[derive(FromArgs)]
struct FineStruct {
    /// Always five.
    #[argp(option, from_str_fn(always_five))]
    five: usize,

    /// File path.
    #[argp(option, from_os_str_fn(convert_path))]
    path: PathBuf,
}

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

fn convert_path(value: &OsStr) -> Result<PathBuf, String> {
    Ok(PathBuf::from("/tmp").join(value))
}

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


/// A command with positional arguments.
#[derive(FromArgs, PartialEq, Debug)]
struct WithPositional {
    #[argp(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:


/// A command with a greedy positional argument at the end.
#[derive(FromArgs, PartialEq, Debug)]
struct WithGreedyPositional {
    /// Some stuff.
    #[argp(option)]
    stuff: Option<String>,
    #[argp(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

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:


/// Top-level command.
#[derive(FromArgs, PartialEq, Debug)]
struct TopLevel {
    /// Be verbose.
    #[argp(switch, short = 'v', global)]
    verbose: bool,

    /// Run locally.
    #[argp(switch)]
    quiet: bool,

    #[argp(subcommand)]
    nested: MySubCommandEnum,
}

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

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

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

Normally the options specified in TopLevel must be placed before the subcommand name, e.g. ./some_bin --quiet one --x 42 will work, but ./some_bin one --quiet --x 42 won’t. To allow an option from a higher level to be used at a lower level (in subcommands), you can specify the global attribute to the option (--verbose in the example above).

Global options only propagate down, not up (to parent commands), but their values are propagated back up to the parent once a user has used them. In effect, this means that you should define all global arguments at the top level, but it doesn’t matter where the user uses the global argument.

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.


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

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

/// Normal subcommand.
#[derive(FromArgs, PartialEq, Debug)]
#[argp(subcommand, name = "normal")]
struct NormalSubCommand {
    /// How many x.
    #[argp(option)]
    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();

            // argp 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_from_args(command_name: &[&str], args: &[&OsStr]) -> Option<Result<Self, EarlyExit>> {
        for command in Self::commands() {
            if command_name.last() == Some(&command.name) {
                if !args.is_empty() {
                    return Some(Err(Error::other("Our example dynamic command never takes arguments!").into()));
                }
                return Some(Ok(Dynamic { name: command.name.to_string() }))
            }
        }
        None
    }
}

§Help message

The formatting of the help message can be customized using the HelpStyle passed as an argument to parse_args_or_exit:


let args: Args = argp::parse_args_or_exit(&HelpStyle {
    blank_lines_spacing: 1,
    description_indent: 8,
    ..HelpStyle::default()
});

Note that the HelpStyle struct may be extended with more fields in the future, so always initialise it using HelpStyle::default() as shown above.

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:


/// Cargo arguments
#[derive(FromArgs)]
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.
    #[argp(positional, hidden_help)]
    command: String,
    /// An option used for internal debugging.
    #[argp(option, hidden_help)]
    internal_debugging: String,
    #[argp(positional)]
    real_first_arg: String,
}

§Markdown

Any descriptions provided as a doc comment (or doc attribute) are interpreted as Markdown and converted to plain text (at build time) as shown in the table below. The output format may change slightly in the future.

MarkdownOutput
# Heading, ## Heading, …Heading
*italic*, _italic_italic
**bold**, __bold__*bold*
`monospace``monospace`
[link title](https://example.org)https://example.org
* unordered list
- nested list
* unordered list
* nested list
1. ordered list
1. nested list
1. ordered list
1. nested list
```
code
block
```
code
block
> block
> quote
>
>> nested
  block
quote

nested
<p>html</p><p>html</p>
Line<br>break
Line
break

If you want to remove an implicit blank line between two blocks, for example a paragraph and a list, you can do this with <br>:

List of items:<br>
* one
* two

This can also be used to write several empty lines in a row, which would otherwise be stripped.

Re-exports§

pub use crate::error::MissingRequirements;
pub use crate::help::CommandInfo;
pub use crate::help::HelpStyle;

Modules§

help
Struct in this module are all used by the generated code. They can be used outside the library, but without any guarantees - there may be breaking changes between minor versions.
parser
Items in this module are all used by the generated code, and should not be considered part of this library’s public API surface.
term_size
This module provides a lightweight, zero-dependencies implementation of a function to get the terminal width on Unix systems. It’s replaced with a no-op implementation on non-unix systems or when the term_size feature is disabled.

Enums§

EarlyExit
Information to display to the user about why a FromArgs construction exited early.
Error
The error type for the argp parser.

Constants§

DEFAULT
A convenient shortcut for HelpStyle::default.

Traits§

CommandHelp
A FromArgs implementation with attached HelpInfo struct.
DynamicSubCommand
Trait implemented by values returned from a dynamic subcommand handler.
FromArgValue
Types which can be constructed from a single command-line value.
FromArgs
Types which can be constructed from a set of command-line arguments.
SubCommand
A FromArgs implementation that represents a single subcommand.
SubCommands
A FromArgs implementation that can parse into one or more subcommands.
TopLevelCommand
A top-level FromArgs implementation that is not a subcommand.

Functions§

cargo_parse_args_or_exit
Create a FromArgs type from the current process’s env::args_os().
from_envDeprecated
Deprecated alias for parse_args_or_exit.
parse_args_or_exit
Create a FromArgs type from the current process’s env::args_os() with the given HelpStyle. For the default help style argp::DEFAULT can be used.

Derive Macros§

FromArgs
Entrypoint for #[derive(FromArgs)].