Crate immargs

Crate immargs 

Source
Expand description

§Immediate Arguments

No-hassle, on-the-spot, command line argument parser

Highlights:

  • Straightforward declaration of arguments with proc-macro.
  • Supports POSIX / GNU argument syntax conventions.
  • Supports arguments of any type that implements FromStr + Debug.
  • Supports (sub)commands, with aliases.
  • Supports declaration of conflicting arguments.
  • Supports automatic --version and --help handling, with possibility to opt-out.
  • Tested on Linux, macOS and Windows.
  • No run-time dependencies.

§Basic Example

This exanple is using immargs_from_env! for on-the-spot declaration and parsing of command line arguments.

use immargs::immargs_from_env;

let args = immargs_from_env! {
    --force               "overwrite destination",
    -l --log <level> u8   "set log level",
    -h --help             "print help message",
    <src>... String       "source(s)",
    <dest> String         "destination",
};

// Assuming this program was executed with "myprog -l 3 Src0 Src1 Dest"
assert!(!args.force);
assert!(args.log == Some(3));
assert!(args.src.len() == 2);
assert!(args.src[0] == "Src0");
assert!(args.src[1] == "Src1");
assert!(args.dest == "Dest");

immargs_from_env! returns a struct with fields derived from to the arguments specification. The fields are populated with the corresponding values from the command line. For the example above, the returned struct looks like this:

pub struct ImmArgs {
    pub force: bool,
    pub log: Option<u8>,
    pub src: Vec<String>,
    pub dest: String,
}

A help message will also be derived from the arguments specification, and is printed if the -h or --help option is used. For the above example, the help message looks like this:

usage: myprog [options] <src>... <dest>

options:
   --force               overwrite destination
   -l, --log <level>     set log level
   -h, --help            print help message

arguments:
   <src>...              source(s)
   <dest>                desination

§Advanced Example

This example is using immargs! to declare command line arguments. The main program takes a command as its last argument, and each (sub)command use immargs! do declare the arguments they each accept. Any arguments following the command will be returned as an Args that is part of the command enum, which is converted into the (sub)command arguments struct using into().

use immargs::immargs;

// The last argument is a command, with available commands (and their aliases) enclosed
// by braces. An `enum`, with variants matching the commands, will be generated.
immargs! {
    MainArgs,                                         // Argument struct will be called "MainArgs"
    -v --verbose           "enable verbose logging",  // An option
    --version              "print version",           // --version enables automatic version printing
    -h --help              "print help message",      // --help enables automatic help printing
    <command> Command      "the command to run" {     // Command enum will be named "Command"
        add                "add file(s)",             // "add" has no aliases
        remove rm          "remove file(s)",          // "rm" is an alias for "remove"
        commit co c        "commit changes",          // "co" and "c" are aliases for "commit"
    }
}

// The "?" indicates that these are conflicting arguments, i.e. they can't be
// used at the same time, but one of them must be present on the command line.
immargs! {
    AddArgs,
    -a --all            ?  "add all files",           // Conflicts with [<file>...]
    --force                "overwrite destination",
    -h --help              "print help message",
    [<file>...] String  ?  "file(s) to add",          // Conflicts with -a, --all
}

immargs! {
    RemoveArgs,
    -r --recursive         "recursively remove files",
    -h --help              "print help message",
    <file>... String       "file(s) to remove",       // "..." means it's a variadic argument
}

immargs! {
    CommitArgs,
    -a --amend             "amend latest commit",
    -h --help              "print help message",
    [<message>] String     "commit message",          // "[ ]" means it's an optional argument
}

fn main() {
    let main_args = MainArgs::from_env();
    let verbose = main_args.verbose;

    match main_args.command {
        Command::Add(args) => add(verbose, args.into()),
        Command::Remove(args) => remove(verbose, args.into()),
        Command::Commit(args) => commit(verbose, args.into()),
    }
}

fn add(verbose: bool, args: AddArgs) {
    // ...
}

fn remove(verbose: bool, args: RemoveArgs) {
    // ...
}

fn commit(verbose: bool, args: CommitArgs) {
    // ...
}

§Additional Examples

Additional examples can be found in examples directory.

§Terminology

Terms and definitions used by immargs (mostly derived from POSIX and GNU definitions):

  • Arguments - The umbrella term for all kinds of strings that appear on the command line. Arguments are further divided into options and non-options.

  • Options - The subset of arguments that start with - or --, e.g. -v or --verbose. The order in which options appear on the command line carries no meaning, i.e. -a -b has the same meaning as -b -a. Options are, as the name implies, always optional and never required. Options are further divided into short and long options.

  • Short options - The subset of options that start with - followed by a single character, e.g. -v.

  • Long options - The subset of options that start with -- followed by two or more characters, e.g. --verbose.

  • Value - An additional piece of information associated with an option, e.g. --speed 100, where 100 is the value. Value-less options implicitly hold a binary value (true or false) through their presence or absence on the command line.

  • Non-options - The subset of arguments that don’t start with - or --, e.g. commit or file.txt. The order in which non-options appear on the command line carries meaning, i.e. commit file.txt doesn’t have the same meaning as file.txt commit. Non-optons are further divided into required and optional non-options.

  • Required non-option - A non-option that must be present on the command line. In text, required non-options are represented by enclosing < >, e.g. <file>.

  • Optional non-option - A non-options that doesn’t have to be present on the command line. In text, optional non-options are represented by enclosing [< >], e.g. [<file>].

  • Variadic option - An option that is materialized from multiple separate instances of the option, as opposed to the last instance taking precedence. E.g. --file hello.txt --file world.txt results in the option file holding two values, hello.txt and world.txt. A value-less option can also be variadic, in which case the value held by the option is an unsigned integer representing the number of times the option appeared on the command line. E.g. --verbose --verbose --verbose results in the option verbose holding the value 3. In text, variadic options are represented by trailing ..., e.g. --file... <path> or --verbose....

  • Variadic non-option - A non-option that is materialized from multiple separate command line arguments of the same type. In text, variadic non-options are represented by trailing ..., e.g. <file>... or [<file>...].

§Command Line Argument Syntax

The following POSIX / GNU argument syntax conventions are supported:

  • Short option, - followed by a single character, e.g. -f.
  • Long option, -- followed by two or more characters, e.g. --foo.
  • Short/Long option with separate value, e.g. -f 100 or --foo 100.
  • Short/Long option with attached value delimited by =, e.g. -f=100 or --foo=100.
  • Short option with attached value without delimiter, e.g. -f100.
  • Combined short options, e.g. -abc is equivalent to -a -b -c.
  • Short/Long options may appear in any order, but must come before any non-option arguments.
  • Short/Long options may appear multiple times, the last appearance takes precedence unless it’s a variadic (repeatable) option, where the number of times the option appears has meaning, e.g. -vvv where each -v increases the verbosity level.
  • The order of non-option arguments carries meaning.
  • A standalone - argument is treated as a non-option argument.
  • A standalone -- argument marks the end of options. Any following arguments are treated as non-option arguments.

§The Arguments struct

§Field Names

The field names of the struct generated by immargs! are derived as follows.

Argument TypeField NameExample
OptionField name is direved from the first long-option (or the first short-option if no long-option exists)--foo uses field name foo
Non-OptionField name is derived from the non-option name<bar> T uses field name bar

Note that the specified option and non-option names must be valid Rust non-keyword identifiers, as they will become the names of the struct fields. However, the names visible to the user of the program will be transformed as follows, to allow use of names that aren’t valid Rust struct field names (keywords, words starting with a number, etc).

  • Any starting and trailing _ will be stripped.
  • Any other _ will be replaced by -.
  • Letters will be converted to lower case (does not apply to short-options).

Examples:

Specified Argumentstruct Field NameUser Visibale Name
-_1_1-1
--move_move_--move
--_1_to_1_1_to_1--1-to-1
--Report_ErrorReport_Error--report-error
--log <Log_Level> u8log--log <log-level>
<number_of_items>number_or_items<number-of-items>
[_4th]_4th[4th]
§Field Types

The field types of the struct generated by immargs! are derived as follows.

Argument TypeExampleField Type
Option--foobool
Option with Value--foo <bar> TOption<T>
Variadic Option--foo...usize
Variadic Option with Value--foo... <bar> TVec<T>
Required Non-option<foo> TT
Optional Non-option[<foo>] TOption<T>
Required Variadic Non-option<foo>... TVec<T>, with length > 0
Optional Variadic Non-option[<foo>...] TVec<T>, with length >= 0
Required Command<foo> T { /* commands */ }T(Args)
Optional Command[<foo>] T { /* commands */ }Option<T(Args)>
§Methods

The following methods are available on structs generated by immargs!.

MethodReturn Type
from_env()Self
from<T: IntoIterator<Item: Into<String>>>(args: T)Self
try_from_env()Result<Self>
try_from<T: IntoIterator<Item: Into<String>>>(args: T)Result<Self>

Most applications would want to use from_env(), which uses arguments provided by std::env::args_os() and on failure prints an error message and terminates the program with an appropriate exit code.

The other methods exist to allow applications to opt-out of the default behaviours provided by from_env(), such as explicltly providing the command line arguments to parse, or to implement custom error and help handling.

§Conflicting Arguments

An argument can be declared to be in conflict with one or more other arguments. This is useful when two or more arguments are incompatible or otherwise mutually exclusive. A conflict is declared using an ! or ? optionally followed by a conflict-id. The conflict-id is an identifier used if there are more than one group of conflicting options. ! (plain conflict) means that zero or one of the options in the group is allowed to be present on the command line, while ? (choice) means that exactly one of the options in the group is must be present on the command line.

Example with one group of conflicting arguments, i.e. without an explicit conflict-id, where exacly one of them must be present on the command line:

use immargs::immargs;

immargs! {
    --verbose,                   // Doesn't conflict with any other argument
    --all                 ?,     // Conflicts with [names...]
    [<names>...] String   ?,     // Conflicts with --all
}

Example with two groups (B_C and C_D_E) of conflicting arguments, where one argument is part of both groups. Exactly one of the options in group B_C must be present, and zero or one of the options in group C_D_E is allowed to be present on the command line.

use immargs::immargs;

immargs! {
    --feature_a,                 // Doesn't conflict with any other argument
    --feature_b   ?B_C,          // Conflicts with --feature-c
    --feature_c   ?B_C !C_D_E,   // Conflicts with --feature-b, --feature-d and --feature-e
    --feature_d   !C_D_E,        // Conflicts with --feature-c and --feature-e
    --feature_e   !C_D_E,        // Conflicts with --feature-c and --feature-d
}

§Help and Version

Options with long-option names --help and --version are special. These options are intercepted during arguments parsing and will not be visible, or have corresponding fields, in the arguments struct generated by immargs!. When parsing the command line using from() or from_env(), these options will cause a version or help message to be displayed and the application will be terminated. When parsing the command line using try_from() or try_from_env() these options will instead generate a Help or Version error, which the application can react to, e.g. if the application wants to display custom version or help messages.

§Unicode

Non-unicode command line arguments will be converted to unicode using to_string_lossy() before they are parsed.

Macros§

immargs
Macro for declaring command line arguments.
immargs_from
Convenience macro for on-the-spot command line argument parsing.
immargs_from_env
Convenience macro for on-the-spot command line argument parsing.
immargs_try_from
Convenience macro for on-the-spot command line argument parsing.
immargs_try_from_env
Convenience macro for on-the-spot command line argument parsing.

Structs§

Args
Command line arguments in raw form, i.e. not yet parsed.

Enums§

Error
Errors returned by argument parser.

Traits§

FromArgs
A trait implemented by all arguments structs generated by immargs!.

Type Aliases§

Result
Result returned by argument parser.