Crate just_getopt

Crate just_getopt 

Source
Expand description

§Introduction

This library crate implements a Posix getopt-like command-line option parser with simple programming interface. More specifically the parser is like getopt’s GNU extension called getopt_long which is familiar command-line option format for users of Linux-based operating systems.

The name is just_getopt because this is just a getopt parser and (almost) nothing more. The intent is to provide the parsed output and basic methods for examining the output. There will not be anything for interpreting the output or for printing messages to program’s user. The responsibility of interpretation is left to your program.

In getopt logic there are two types of command-line options:

  1. short options with a single letter name (-f)
  2. long options with more than one letter as their name (--file).

Both option types may accept an optional value or they may require a value. Values are given after the option. See the section Parsing Rules below for more information.

Programming examples are in the Examples section below and in the source code repository’s “examples” directory.

§Parsing Rules

By default, all options are expected to come first in the command line. Other arguments (non-options) come after options. Therefore the first argument that does not look like an option stops option parsing and the rest of the command line is parsed as non-options. This default can be changed, so that options and non-options can be mixed in their order in the command line. See OptSpecs::flag method for more information.

In command line the “pseudo option” -- (two dashes) always stops the option parser. Then the rest of the command line is parsed as regular arguments (non-options).

§Short Options

Short options in the command line start with the - character which is followed by option’s name character (-c), usually a letter.

If option requires a value the value must be entered either directly after the option character (-cVALUE) or as the next command-line argument (-c VALUE). In the latter case anything that follows -c will be parsed as option’s value.

If option accepts an optional value the value must always be entered directly after the option character (-cVALUE). Otherwise there is no value for this option.

Several short options can be entered together after one - character (-abc) but then only the last option in the series may have required or optional value.

§Long Options

Long options start with -- characters and the option name comes directly after it (--foo). The name must be at least two characters long.

If option requires a value the value must be entered either directly after the option name and = character (--foo=VALUE) or as the next command-line argument (--foo VALUE). In the latter case anything that follows --foo will be parsed as option’s value.

If option accepts an optional value the value must always be entered directly after the option name and = character (--foo=VALUE). Otherwise there is no value for this option.

Option --foo= is valid format when the option requires a value or accepts an optional value. It means that the value is empty string. It is not valid format when the option does not accept a value.

§Examples

Following examples will guide through a typical use of this library crate and command-line parsing.

§Prepare

First we bring some important paths into the scope of our program.

use just_getopt::{OptFlags, OptSpecs, OptValue};

Then we define which command-line options are valid for the program. We do this by creating an instance of OptSpecs struct by calling function OptSpecs::new. Then we modify the struct instance with option and flag methods.

let specs = OptSpecs::new()
    .option("help", "h", OptValue::None) // Arguments: (id, name, value_type)
    .option("help", "help", OptValue::None)
    .option("file", "f", OptValue::RequiredNonEmpty)
    .option("file", "file", OptValue::RequiredNonEmpty)
    .option("verbose", "v", OptValue::OptionalNonEmpty)
    .option("verbose", "verbose", OptValue::OptionalNonEmpty)
    .flag(OptFlags::OptionsEverywhere);

Each option method above adds a single option information to the option specification. Method’s arguments are:

  1. id: Programmer’s identifier string for the option. The same identifier is used later to check if this particular option was present in the command line. Several options may have the same id. This makes sense when short option and long option have the same meaning, like -h and --help for printing help.

  2. name: Option’s name string in the command line, without prefix. A single-character name (like h) defines a short option which is entered like -h in the command line. Longer name defines a long option which is entered like --help in the command line.

  3. value_type: Whether or not this option accepts a value and if the value is optional or required. The argument is a variant of enum OptValue.

The flag method above adds a configuration flag for the command-line parser. It is a variant of enum OptFlags. This variant OptionsEverywhere changes the command-line parser to accept options and other arguments in mixed order in the command line. That is, options can come after non-option arguments.

For better explanation see the documentation of OptSpecs struct and its methods option and flag. Also see methods limit_options, limit_other_args and limit_unknown_options.

§Parse the Command Line

We are ready to parse program’s command-line arguments. We do this with OptSpecs::getopt method. Arguments we get from std::env::args function which returns an iterator.

// Get arguments iterator from operating system and skip the first item
let args = std::env::args().skip(1); // which is this program's file path.
let parsed = specs.getopt(args); // Getopt! Use the "specs" variable defined above.

If you want to try getopt method without program’s real command-line arguments you can also run it with other iterator argument or with a vector or an array as an argument. Like this:

let parsed = specs.getopt(["--file=123", "-f456", "foo", "-av", "bar"]);

§Examine the Parsed Output

The command line is now parsed and the variable parsed (see above) owns an Args struct which represents the parsed output in organized form. It is a public struct and it can be examined manually. There are some methods for convenience, and some of them are shown in the following examples.

At this stage it is useful to see the returned Args struct. One of its fields may contain some Opt structs too if the parser found valid command-line options. Let’s print it:

eprintln!("{:#?}", parsed);

That could print something like this:

Args {
    options: [
        Opt {
            id: "file",
            name: "file",
            value_required: true,
            value: Some(
                "123",
            ),
        },
        Opt {
            id: "file",
            name: "f",
            value_required: true,
            value: Some(
                "456",
            ),
        },
        Opt {
            id: "verbose",
            name: "v",
            value_required: false,
            value: None,
        },
    ],
    other: [
        "foo",
        "bar",
    ],
    unknown: [
        "a",
    ],
}

§Check for Bad Options

Usually we want to check if there were bad options, and if so, exit the program with friendly help messages. We create a flag variable to hold the condition if we need to exit.

let mut error_exit = false;

Print error messages about possible unknown options.

for u in &parsed.unknown {
    eprintln!("Unknown option: {u}");
    error_exit = true;
}

Print error message about possible missing values for options which require a value.

for o in parsed.required_value_missing() {
    eprintln!("Value is required for option '{}'.", o.name);
    error_exit = true;
}

Exit the program if there were bad options (see above).

if error_exit {
    eprintln!("Use '-h' for help.");
    std::process::exit(1);
}

Command-line programs always have -h or --help option for printing a friendly help message. The following example shows how to detect that option.

if parsed.option_exists("help") {
    println!("Print friendly help about program's usage.");
    std::process::exit(0);
}

The "help" string in the first line above is the identifier string (id) for the option. It was defined with OptSpecs::option method in the example code earlier. Identifier strings are used to find if a specific option was given in the command line.

§Collect Values and Other Arguments

The rest depends very much on individual program’s needs. Probably often we would collect what values were given to options. In our example program there are -f and --file options that require a value. We could collect all those values next.

for f in parsed.options_value_all("file") {
    println!("File name: {:?}", f);
}

Notice if -v or --verbose was given, even without a value. Then collect all (optional) values for the option.

if parsed.option_exists("verbose") {
    println!("Option 'verbose' was given.");
    for v in parsed.options_value_all("verbose") {
        println!("Verbose level: {:?}", v);
    }
}

Finally, our example program will handle all other arguments, that is, non-option arguments.

for o in &parsed.other {
    println!("Other argument: {:?}", o);
}

§More Help

A complete working example code – very similar to previous examples – is in the source code repository’s “examples” directory. It can be run with command cargo run --example basic -- your arguments. Try it with different command-line arguments.

Further reading:

  • OptSpecs struct and its methods.
  • Args struct and its methods.

Structs§

Args
Parsed command line in organized form.
Opt
Structured option information.
OptSpecs
Specification for program’s valid command-line options.

Enums§

OptFlags
Flags for changing command-line parser’s behavior.
OptValue
Option’s value type.