argparsnip 0.1.6

A small Argparser
Documentation
# Parsnip

A small Rust Argparser

```
// ./prog --arg param1 param2 -a
fn main() {
    let args = args! {
        // define all your args here
        args: vec![arg! {
            name: "arg",
            short: Some("a"),
        }, arg! {
            name: "arg2",
            long: Some("arg2"),
            num_values: NumValues::Between(1, 3),
        }],
        // subcommands (e.g git add)
        subcommands: vec![args! {}],
        // optional, by default parsing will return a "Results" object
        handler: |results| { return results.args.get("arg2"); }
    };
    let results = args.parse(std::env::args());
}
```

# Setup
Add the lib to your `Cargo.toml`.

```
[dependencies]
argparsnip = "0.1.5"
```

# Features

* **Autogenerated help/version commands**
  - can be overwritten
  - <program> [subcommand]* help & <program> [subcommand]* version commands are also generated
  - name/version/about parameters default to using Cargo.toml `CARGO_PKG_NAME/CARGO_PKG_VERSION/CARGO_PKG_DESCRIPTION` variables
* **Arguments**
  - supports short `-h` and `--help` syntax
  - unicode support by default
  - supports returning (as a vec) or failing on unknown arguments
  - **Flags**
    - supports combinations (e.g `-rli` is the same as `-r -l -i`)
    - supports repeats, e.g `-vvv -v` will count as the same flag `v` appearing 4 times
  - **With Values**
    - supports constraints on the number of values for an arg, e.g having exactly one value, having between 2 & 4 values, having any number of values. E.g `-v foo bar`
    - supports restricting values to specific *primitive* types (any, bool, i32, i64, f32, f64, String) via the TryInto trait
    - supports additional custom validation (so you can e.g write your own sum type restrictions)
    - supports default values
  - **Combinations**
    - Parsing can be configured to fail if any required argument is missing
    - supports requiring at least one of a set of arguments (e.g A || B || C)
    - supports requiring all arguments in a set (e.g A && B && C)
    - supports inverting sets (e.g !(A && B))
    - supports requiring any or all of multiple sets (e.g (A && B) || (A && C)). This can also be negated
  - **Positional**
    - supports unix `--`, i.e `foo -- -a -b -c` will recieve positional arguments ["-a", "-b", "-c"]
    - treats all args that don't start with `-` or `--` as positional.
* **Subcommands**
  - e.g cargo run vs cargo test (run/test are subcommands)
  - subcommands can have their own subcommands and arguments
  - help/version commands are generated separately for each subcommand
* **Optional Callback support**
  - Instead of returning a results object with the argparsing results, A handler fn(Results) -> T can be provided for each command/subcommand (not supported when using serde).
* **no_std support**
  - disable default features to enable no_std
  - `parsnip = { version = "x", default-features = false }`
* **serde support**
  - `parsnip = { version = "x", features = ["derive"] }`
  - write your args schema in any format with a serde parser (serde_json, toml etc.), see derive-test for an example
* **Other opt-in features**
  - *debug* - enables logging info about arg parsing
  - *macros* - enabled by default, we provide some utility macros to avoid writing ..Default::default() everywhere

# Usage

Here are some quick common cases. For more examples please look at the tests in `lib.rs`

## Documentation
https://docs.rs/argparsnip/0.1.5/argparsnip/

## Examples

**Minimal Flag Example**

```
// ./prog --arg
fn main() {
    let args = args! {
        args: vec![arg! {
            name: "arg",
            short: Some("a"),
        }],
    };
    let results = args.parse(std::env::args());
    assert_eq!(1, results.flags("arg"));
}
```

**Check if a flag was given once**

```
// ./prog --arg
fn main() {
    let args = args! {
        args: vec![arg! {
            name: "arg",
            short: Some("a"),
            about: "a flag",
            long: Some("arg"),
            required: true,
        }],
    };
    let results = args.parse(std::env::args());
    assert_eq!(1, results.flags("arg"));
}
```

**Get the value of an arg**

```
// ./prog -a 1
fn main() {
    let args = args! {
        args: vec![arg! {
            name: "arg",
            short: Some("a"),
            default: Some(|| { Value::From(2) }),
            value_type: Type::Int,
            num_values: NumValues::Fixed(1),
        }],
    };
    let results = args.parse(std::env::args());
    assert_eq!(1, results.params.get("arg")?.try_into());
}
```

**Validate an argument**

```
// ./prog -a 1 2
fn main() {
    let args = args! {
        args: vec![arg! {
            name: "arg",
            short: Some("a"),
            value_type: Type::Int,
            num_values: NumValues::AtLeast(1),
            validation: |val| {
                let val: &i32 = v.try_into().unwrap();
                if 2 >= *val {
                    Ok(())
                } else {
                    Err("failed validation")
                }
            }
        }],
    };
    let results = args.parse(std::env::args());
    assert_eq!(vec![1, 2], results.params.get("arg")?.try_into());
}
```

**Using Subcommand**

```
// ./prog sub --arg 
fn main() {
    let args = args! {
        args: vec![arg! {
            name: "arg",
            long: Some("arg"),
            num_values: NumValues::None,
        }],
        subcommands: vec![args! {
            name: "sub",
            path: Some("main/sub"),
            args: vec![arg! {
                name: "arg",
                long: Some("arg"),
                num_values: NumValues::None,
            }],
        }],
    };
    let results = args.parse(std::env::args());
    // this is the unique identifier for the subcommand
    assert_eq!("main/sub", results.path);
    assert_eq!(1, results.flags["arg"]);
}
```

**Filters**
```
// only supports combinations (--arg && --arg2) or (--arg && --arg3)
// will fail if --arg or --arg2 or --arg3 are passed on their own
fn main() {
    let args = args! {
        args: vec![arg! {
            name: "arg",
            long: Some("arg"),
            num_values: NumValues::None,
        }, arg! {
            name: "arg2",
            long: Some("arg2"),
            num_values: NumValues::None,
        }, arg! {
            name: "arg3",
            long: Some("arg3"),
            num_values: NumValues::None,
        }],
        filters: Filters {
            filters: vec![Filter {
                filter_type: FilterType::All,
                inverse: false,
                args: vec!["arg", "arg2"],
            }, Filter {
                filter_type: FilterType::All,
                inverse: false,
                args: vec!["arg", "arg3"],
            }],
            ..Default::default()
        },
        // this flag means we will fail if we see the same value multiple times
        disable_overrides: true,
    };
    let results = args.parse(std::env::args());
}
```

# Development

## TODO

* Benchmarks
* More tests
* Features
  * Bash/Zsh completion
  * Support disabling positional args
  * Support updating repeats, e.g --arg Foo --arg Bar should give {"arg": ["Foo", "Bar"]} 
* Pretty sure my design isn't iterator friendly, try using a counter instead