Giter Club home page Giter Club logo

rust-argparse's Introduction

Argparse

The rust-argparse is a command-line parsing module for Rust. It's inspired by Python's argparse module.

Features:

  • Supports standard (GNU) option conventions
  • Properly typed values
  • Automatically generated help and usage messages

Importing

Edit your Cargo.toml to add rust-argparse to your project.

[dependencies]
argparse = "0.2.2"

Example

The following code is a simple Rust program with command-line arguments:

extern crate argparse;

use argparse::{ArgumentParser, StoreTrue, Store};

fn main() {
    let mut verbose = false;
    let mut name = "World".to_string();
    {  // this block limits scope of borrows by ap.refer() method
        let mut ap = ArgumentParser::new();
        ap.set_description("Greet somebody.");
        ap.refer(&mut verbose)
            .add_option(&["-v", "--verbose"], StoreTrue,
            "Be verbose");
        ap.refer(&mut name)
            .add_option(&["--name"], Store,
            "Name for the greeting");
        ap.parse_args_or_exit();
    }

    if verbose {
        println!("name is {}", name);
    }
    println!("Hello {}!", name);
}

Assuming the Rust code above is saved into a file greeting.rs, let's see what we have now:

$ rustc greeting.rs
$ ./greeting -h
Usage:
  ./greeting [OPTIONS]

Greet somebody.

Optional arguments:
  -h, --help  Show this help message and exit
  -v, --verbose
             Be verbose
  --name NAME Name for the greeting
$ ./greeting
Hello World!
$ ./greeting --name Bob
Hello Bob!
$ ./greeting -v --name Alice
name is Alice
Hello Alice!

Basic Workflow

Create ArgumentParser

The argument parser is created empty and is built incrementally. So we create a mutable variable:

extern crate argparse;
use argparse::ArgumentParser;

let mut parser = ArgumentParser::new();

Customize

There are optional customization methods. The most important one is:

parser.set_description("My command-line utility");

The description is rewrapped to fit 80 column string nicely. Just like option descriptions.

Add Options

The refer method creates a cell variable, which the result will be written to:

let mut verbose = false;
parser.refer(&mut verbose);

Next we add options which control the variable. For example:

parser.refer(&mut verbose)
    .add_option(&["-v", "--verbose"], StoreTrue,
                "Be verbose");

You may add multiple options for the same variable:

parser.refer(&mut verbose)
    .add_option(&["-v", "--verbose"], StoreTrue,
                "Be verbose")
    .add_option(&["-q", "--quiet"], StoreFalse,
                "Be verbose");

Similarly positional arguments are added:

let mut command = String::new();
parser.refer(&mut command)
    .add_argument("command", Store,
                  "Command to run");

Organizing Options

It's often useful to organize options into some kind of structure. You can easily borrow variables from the structure into option parser. For example:

struct Options {
    verbose: bool,
}
// ...
let mut options = Options { verbose: false };
parser.refer(&mut options.verbose)
    .add_option(&["-v"], StoreTrue,
                "Be verbose");

Parsing Arguments

All the complex work is done in parser.parse_args(). But there is a simpler option:

parser.parse_args_or_exit();

In case you don't want argparse to exit itself, you might use the parse_args function directly:

use std::process::exit;

match parser.parse_args() {
    Ok(()) => {}
    Err(x) => {
        std::process::exit(x);
    }
}

ArgumentParser Methods

parser.refer<T>(var: &mut T) -> Ref

Attach the variable to the argument parser. The options are added to the returned Ref object and modify a variable passed to the method.

parser.add_option(names: &[&str], action: TypedAction, help: &str)

Add a single option which has no parameters. Most options must be added by refer(..) and methods on Ref object (see below).

Example:

ap.add_option(&["-V", "--version"],
    Print(env!("CARGO_PKG_VERSION").to_string()), "Show version");

parser.set_description(descr: &str)

Set description that is at the top of help message.

parser.stop_on_first_argument(val: bool)

If called with true, parser will stop searching for options when first non-option (the one doesn't start with -) argument is encountered. This is useful if you want to parse following options with another argparser or external program.

parser.silence_double_dash(val: bool)

If called with true (default), parser will not treat first double dash -- as positional argument. Use false if you need to add some meaning to the -- marker.

parser.print_usage(name: &str, writer: &mut Write)

Print usage string to stderr.

parser.print_help(name: &str, writer: &mut Write)

Writes help to writer, used by --help option internally.

parser.parse_args()

Method that does all the dirty work and returns Result.

parser.parse_args_or_exit()

Method that does all the dirty work and in case of failure just exit().

Variable Reference Methods

The argparse::Ref object is returned from parser.refer(). The following methods are used to add and customize arguments:

option.add_option(names: &[&str], action: TypedAction, help: &str)

Add an option. All items in names should be either in format -X or --long-option (i.e. one dash and one char or two dashes and long name). How this option will be interpreted and whether it will have an argument dependes on the action. See below list of actions.

option.add_argument(name: &str, action: TypedAction, help: &str)

Add a positional argument.

option.metavar(var: &str)

A name of the argument in usage messages (for options having argument).

option.envvar(var: &str)

A name of the environment variable to get option value from. The value would be parsed with FromStr::from_str, just like an option having Store action.

option.required()

The option or argument is required (it's optional by default). If multiple options or multiple arguments are defined for this reference at least one of them is required.

Actions

The following actions are available out of the box. They may be used in either add_option or add_argument:

Store

An option has single argument. Stores a value from command-line in a variable. Any type that has the FromStr and Clone traits implemented may be used.

StoreOption

As Store, but wrap value with Some for use with Option. For example:

let mut x: Option<i32> = None; ap.refer(&mut x).add_option(&["-x"], StoreOption, "Set var x");

StoreConst(value)

An option has no arguments. Store a hard-coded value into variable, when specified. Any type with the Clone trait implemented may be used.

PushConst(value)

An option has no arguments. Push a hard-coded value into variable, when specified. Any type which has the Clone trait implemented may be used. Option might used for a list of operations to perform, when required is set for this variable, at least one operation is required.

StoreTrue

Stores boolean true value in a variable. (shortcut for StoreConst(true))

StoreFalse

Stores boolean false value in a variable. (shortcut for StoreConst(false))

IncrBy(num)

An option has no arguments. Increments the value stored in a variable by a value num. Any type which has the Add and Clone traits may be used.

DecrBy(num)

Decrements the value stored in a variable by a value num. Any type which has the Sub and Clone traits may be used.

Collect

When used for an --option, requires single argument. When used for a positional argument consumes all remaining arguments. Parsed options are added to the list. I.e. a Collect action requires a Vec<int> variable. Parses arguments using FromStr trait.

List

When used for positional argument, works the same as List. When used as an option, consumes all remaining arguments.

Note the usage of List is strongly discouraged, because of complex rules below. Use Collect and positional options if possible. But usage of List action may be useful if you need shell expansion of anything other than last positional argument.

Let's learn rules by example. For the next options:

ap.refer(&mut lst1).add_option(&["-X", "--xx"], List, "List1");
ap.refer(&mut lst2).add_argument("yy", List, "List2");

The following command line:

./run 1 2 3 -X 4 5 6

Will return [1, 2, 3] in the lst1 and the [4, 5, 6] in the lst2.

Note that using when using = or equivalent short option mode, the 'consume all' mode is not enabled. I.e. in the following command-line:

./run 1 2 -X3 4 --xx=5 6

The lst1 has [3, 5] and lst2 has [1, 2, 4, 6]. The argument consuming also stops on -- or the next option:

./run -X 1 2 3 -- 4 5 6
./run -X 1 2 --xx=3 4 5 6

Both of the above parse [4, 5, 6] as lst1 and the [1, 2, 3] as the lst2.

Print(value)

Print the text and exit (with status 0). Useful for the --version option:

ap.add_option(&["-V", "--version"],
        Print(env!("CARGO_PKG_VERSION").to_string()), "Show version");

rust-argparse's People

Contributors

abspoel avatar c-ezra-m avatar d-e-s-o avatar dario23 avatar dhardy avatar faern avatar frewsxcv avatar iamed2 avatar jirutka avatar kagia avatar madeindjs avatar naufraghi avatar oh-its-jimjam avatar savage-engineer avatar tailhook avatar teotwaki avatar thejakeschmidt avatar timnn avatar tomislater avatar vadz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rust-argparse's Issues

List possible subcommands for one argument

Wouldnt it be better if theres the possibility to list all the subcommands which can be used as "COMMAND" and each of them would have an own description? - instead of "command Command to run (either "play" or "record")".

Like this:

command short summing up description
  play description for "play"
  record description for "record"

Cargo manifest?

It'd be nice to have one to be able to use this through Cargo! :)

POSIX style options

Is it possible to use POSIX style options? When I tried the example and used -verbose instead of --verbose, I get a runtime error.

Future of the library

I was used to thinking that this library is not needed anymore because clap and structopt do the job just well. But there are valid points by @d-e-s-o, the most important of it is that clap is very large in size.

On the other hand, I very much like the model structopt provides (i.e. parse in the structure instead of complex borrows). The actual semantics of structopt annotations are sometimes ugly though because it is a thin wrapper around clap (with different actual syntax).

So we have a few options:

  1. Continue support of argparse as is (we need to write rust-style docs, as well as fix few current bugs, requiring breaking changes)
  2. Make structopt-like wrapper (I mean derive) on top of argparse-based parser
  3. Try to optimize clap+structopt if possible

.required() on Collect argument does not enforce at least one value

I set .required() on an argument configured to Collect, with the expectation that an error would occur if the user did not pass in at least one value. This does not appear to be the case, as can be demonstrated by running the example program without any arguments:

extern crate argparse;

use argparse::{ArgumentParser, Collect};

fn main() {
    let mut input_files : Vec<String> = Vec::new();

    {
        let mut ap = ArgumentParser::new();
        ap.set_description("compile a program.");
        ap.refer(&mut input_files)
            .add_argument("input-files", Collect, "one or more input files")
            .required();
        ap.parse_args_or_exit();
    }

    println!("success");
}

License?

As it is currently, rust-argparse is a proprietary code, since that's what missing license means.
I think that it's not the desired state of things?

If you are not sure which one to pick, there's https://tldrlegal.com/

Depending on what you want, you most likely may want to pick one of "main" licenses:

Fails to compile.

This currently fails to compile properly. I tried fixing it, but I am not yet familiar enough with Rust and the (too simple?) fix caused more errors.

The error message is:

argparse/action.rs:13:14: 13:25 error: explicit lifetime bound required
argparse/action.rs:13     Flag(Box<IFlagAction>),
                                   ^~~~~~~~~~~
argparse/action.rs:14:16: 14:26 error: explicit lifetime bound required
argparse/action.rs:14     Single(Box<IArgAction>),
                                     ^~~~~~~~~~
argparse/action.rs:15:14: 15:25 error: explicit lifetime bound required
argparse/action.rs:15     Push(Box<IArgsAction>),
                                   ^~~~~~~~~~~
argparse/action.rs:16:14: 16:25 error: explicit lifetime bound required
argparse/action.rs:16     Many(Box<IArgsAction>),
                                   ^~~~~~~~~~~
argparse/parser.rs:75:17: 75:27 error: explicit lifetime bound required
argparse/parser.rs:75     action: Box<IArgAction>,
                                      ^~~~~~~~~~
argparse/parser.rs:133:23: 133:29 error: explicit lifetime bound required
argparse/parser.rs:133     stderr: &'ctx mut Writer,
                                             ^~~~~~
argparse/parser.rs:596:21: 596:35 error: wrong number of lifetime parameters: expected 1, found 0 [E0107]
argparse/parser.rs:596     pub fn new() -> ArgumentParser {
                                           ^~~~~~~~~~~~~~
argparse/parser.rs:713:18: 713:24 error: explicit lifetime bound required
argparse/parser.rs:713     buf: &'a mut Writer,
                                        ^~~~~~

Library should contain no println!

I am going to use this library for command line program that pipes its stdout to another unix program.
The problem is that piping is done in binary and therefore second program also tries to consume data from this line:

https://github.com/tailhook/rust-argparse/blob/master/src/parser.rs
println!("OPTNAME {:?} {:?}", arg, optname);

Typically such programs print its information output to stderr instead on stdout. Beside this line gave me not very informative results:

let mut options = Options {
        mode: String::new(),
        samplerate: 0,
        inputtype: String::new(),

        shift: 0,
    };

    {
        let mut ap = ArgumentParser::new();
        ap.set_description("commands");

        ap.refer(&mut options.mode)
            // positional
            .add_argument("mode", Store, "<const> or <track> operating mode")
            .required();

        ap.refer(&mut options.samplerate)
            .add_option(&["--samplerate"], Store, "IQ data samplerate")
            .required();

        ap.refer(&mut options.inputtype)
            .add_option(&["--intype"], Store, "IQ data type <i16, f32>")
            .required();

        match ap.parse_args() {
            Ok(()) => {}
            Err(x) => {
                exit(x);
            }
        }
    }
andres@___:~/doppler$ ./target/debug/doppler --intype i16 --samplerate 1234
OPTNAME "--intype" "--intype"
OPTNAME "--samplerate" "--samplerate"
Usage:
    ./target/debug/doppler [OPTIONS] MODE
./target/debug/doppler: Argument mode is required

What do you think about it, should it be removed?

Parsing result error not shown on cli

Nearly all types implementing FromStr return an error that implements the Display/Debug trait, which is an error that could be shown to the user. Currently the error is merely thrown away. Did you have any plans to actually show this on the screen?

Update on crates.io?

Hey, I'm using this library for a cli app, but I need to use PushConst with a type that implements Clone, but not Copy. #4c20997 removes this requirement, but that hasn't been published to crates.io yet. Would you mind publishing the latest version on crates.io? Thanks!

Rust 1.2.0 unresolved import `std::process::exit()`

I tried recently tried to build one of my projects which uses this library with rust 1.2.0 and ran into this compile error.

alexbarbur@hammond ~/p/rust-euler> cargo build
    Updating registry `https://github.com/rust-lang/crates.io-index`
 Downloading argparse v0.2.0
   Compiling argparse v0.2.0
/Users/alexbarbur/.cargo/registry/src/github.com-1ecc6299db9ec823/argparse-0.2.0/src/parser.rs:13:5: 13:23 error: unresolved import `std::process::exit`. There is no `exit` in `std::process`
/Users/alexbarbur/.cargo/registry/src/github.com-1ecc6299db9ec823/argparse-0.2.0/src/parser.rs:13 use std::process::exit;
                                                                                                      ^~~~~~~~~~~~~~~~~~
error: aborting due to previous error
Could not compile `argparse`.

To learn more, run the command again with --verbose.

You can reproduce it like so:

$ git clone https://github.com/CtrlC-Root/rust-euler.git
$ cd rust-euler
$ cargo build

I've been googling this for a while now and I can't seem to figure out if std::process::exit is deprecated or if there's something wrong with the version of rust I'm using. Any ideas?

Fix the tests to work with rust nightly

The new borrow rules does not allow immutable borrow while a mutable borrow is still in effect. So a lot of unit test refactoring have to be done.

The rust-0.13 branch

[BUG] Panic on call "parse_args_or_exit()"

I build this code as binary file.

extern crate argparse;

use argparse::{ArgumentParser, Store, StoreTrue};

fn main() -> std::io::Result<()> {
    let mut write_filename = String::new();
    let mut tty_device = String::new();

    {
        let mut ap = ArgumentParser::new();
        ap.refer(&mut tty_device)
            .add_option(&[], Store, "tty device")
            .required();
        ap.parse_args_or_exit();
    }

    Ok(())
}

I called my_bin and got panic:

./target/debug/my_bin
thread 'main' panicked at 'index out of bounds: the len is 0 but the index is 0', /home/pc/.cargo/registry/src/github.com-1ecc6299db9ec823/argparse-0.2.1/src/parser.rs:517:40
note: Run with `RUST_BACKTRACE=1` for a backtrace.
Process exited with code 101.

I think the problem is contained on this line:

.add_option(&[], Store, "tty device")

Space after comma in help

It would be nicer if it prints a space after the comma in the help. Now with --help the output is:

Optional arguments:
  -h,--help             Show this help message and exit
  -d,--dt DT            Dead-time duration (default 10 000), ns
  -L,--hw HW            Histogram size in channels (default: 1000)
  -s,--show-hist        Show time distribution histogram

and it would be nicer with:

Optional arguments:
  -h, --help             Show this help message and exit
  -d, --dt DT            Dead-time duration (default 10 000), ns
  -L, --hw HW            Histogram size in channels (default: 1000)
  -s, --show-hist        Show time distribution histogram

I think that the edit should be in src/parser.rs on line 864:

    pub fn print_option(&mut self, opt: &GenericOption<'b>) -> IoResult<()> {
        let mut num = 2;
        try!(write!(self.buf, "  "));
        let mut niter = opt.names.iter();
        let name = niter.next().unwrap();
        try!(write!(self.buf, "{}", name));
        num += name.len();
        for name in niter {
864:        try!(write!(self.buf, ","));  // <-- add a space here
            try!(write!(self.buf, "{}", name));
            num += name.len() + 1;
        }
...

Thank you for the great work and the nice crate! I prefer it to the other argument parsing crates because it is quite small and easy to use.

Sync to Cargo.io

Just noticed that the argparse code in Cargo.toml is not in sync with what's in your github repo. When you get time, please could you sync it.

Argument grouping support

It would be nice if argparse supports commands.
For example lets say I have a application that has 2 operating modes: const and tracking. Each mode has its own args and some of them are required args.
Here is an example where I tried to migrate my application from docopt to argparese. Argument shift should be available only for const mode. My approach was to use shift as type Option, however it seems to be quite difficult to implement it that way. Therefore currently I will use shift as u32 and init with 0xFFFFFFFF which means NA.

NB: maybe it is not that difficult and I just do not know the right way. Anyway here is a snippet what I tried to do:

use std::str::FromStr;

use argparse::{ArgumentParser, StoreTrue, Store};
use std::process::exit;
use self::OperatingMode::{TrackMode, ConstMode, UninitializedMode};

#[derive(PartialEq, Eq, Debug)]
pub enum OperatingMode {
    TrackMode,
    ConstMode,
    UninitializedMode,
}

impl FromStr for OperatingMode {
    type Err = ();
    fn from_str(src: &str) -> Result<OperatingMode, ()> {
        return match src {
            "track" => Ok(TrackMode),
            "const" => Ok(ConstMode),
            _ => Err(()),
        };
    }
}

pub struct Options {
    pub mode: OperatingMode,
    pub samplerate: u32,
    pub inputtype: String,

    pub shift: Option,
}

/*
static USAGE: &'static str = "
doppler <[email protected]>

Usage:
    doppler (const (--samplerate <sps> | -s <sps>) --intype <type> --shift <Hz>)
    doppler (track (--samplerate <sps> | -s <sps>) --intype <type> --tlefile <file> --tlename <name> --location <lat,lon,alt> --freq <Hz>) [--time <Y-m-dTH:M:S>] [--shift <Hz>]
    doppler (-h | --help | --version)

Options:
    -s --samplerate <sps>       IQ data samplerate.
    --intype <type>             IQ data type <i16, f32>.

    -h --help                   Show this screen.
    --version                   Show version.

Const mode options:
    --shift <Hz>                Constant frequency shift in Hz [default: 0].

Track mode options:
    --tlefile <file>            TLE database file eg. \"http://www.celestrak.com/NORAD/elements/cubesat.txt\".
    --tlename <name>            TLE name eg. 'ESTCUBE 1'.
    --location <lat,lon,alt>    Observer location on earth.
    --time <Y-m-dTH:M:S>        Observation start time. It should be specified if input is IQ data recording. Real time is used otherwise.
    --freq <Hz>                 Satellite transmitter frequency in Hz.
    --shift <Hz>                Constant frequency shift in Hz [default: 0].
";

*/
pub fn args() -> Options {
    let mut options = Options {
        mode: UninitializedMode,
        samplerate: 0,
        inputtype: String::new(),

        shift: None,
    };

    {
        let mut ap = ArgumentParser::new();
        ap.set_description("commands");

        ap.refer(&mut options.mode)
            // positional
            .add_argument("mode", Store, "<const, track> operating mode")
            .required();

        ap.refer(&mut options.samplerate)
            .add_option(&["--samplerate"], Store, "IQ data samplerate")
            .metavar("<sps>")
            .required();

        ap.refer(&mut options.inputtype)
            .add_option(&["--intype"], Store, "IQ data type")
            .metavar("<i16, f32>")
            .required();

        ap.refer(&mut options.shift)
            .add_option(&["--shift"], Store, "Constant frequency shift in Hz")
            .metavar("<Hz>");

        match ap.parse_args() {
            Ok(()) => {}
            Err(x) => {exit(x);}
        }
    }

    match options.mode {
        TrackMode => {

        }
        ConstMode => {
            //match options.shift {
            //    Some(shift) => {}
            //    None => {println!("--shift", );}
            //}
        }
        _ => {exit(1)}
    }
    options
}

Support for Choices

Python's argparse has support for choices. Could you please add StoreChoice<'a, T: ToString + FromStr>(&'a [T]) which are automatically shown in the value's description and verified accordingly?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.