Skip to content

revellan/revparse

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

revparse

Compile time errors, fast parsing, easy usage.

Usage

This argument parser works using the revparse! macro, into which you will write all the options:

... is just a placeholder.

use revparse::revparse;
revparse! {
    [...];
    [...];
    [...];
}

revparse! {...} should be written outside of any functions, as it creates a module called revmod by default. You can change the name of the module using the ModName setting.

If you want a keyword, like fn as a flag (--fn), read this.

Quickstart

Here is a quick example, a more thorough explanation is below.

use revparse::revparse;
revparse! {
    // arg is of type `bool`
    [arg, 'a', "help message for --arg / -a"];
    // underscores are shown as '-'.
    // with_underscore is also of type `bool`
    [with_underscore, 'w', "help message for --with-underscore / -w"];
    // takes_val has the type `Option<String>`
    [takes_val, 't', "help message for --takes-val=VAL", "VAL"];
}
fn main() {
    let args = revmod::Revparse::new();
    // In this case the user entered no arguments
    assert_eq!(args.arg, false);
    assert_eq!(args.with_underscore, false);
    assert_eq!(args.takes_val, None);
}

There's four different types of non positional arguments you can add:

1. long (--some-arg), short (-s), help message

[some_arg, 's', "help message"];

2. long (--no-short), help message

[no_short, "help message"];

3. long (--value=VALUE), short (-v VALUE / -vVALUE), help message, VALUE

VALUE is for making the user understand what value you want him to enter. For example FILE would mean, the user should enter a filename.

[value, 'v', "help message", "VALUE"];

4. long (--takes-val-no-short=SOME), help message, SOME

[takes_val_no_short, "help message", "SOME"];

All the above options together:

revparse! {
    [some_arg, 's', "help message"];
    [no_short, "help message"];
    [value, 'v', "help message", "VALUE"];
    [takes_val_no_short, "help message", "SOME"];
}

How the help message would look like as of now:

Usage: program_name [OPTION]...

Options:
  -h, --help                display this help text and exit
  -s, --some-arg            help message
  --no-short                help message
  -v, --value=VALUE         help message
  --takes-val-no-short=SOME  help message

Of course "help message" isn't a very useful help message.

As you can see in the help message, it says program_name, which probably isn't what you want.

You can change it using the ExecName setting.

Accessing the values

new() parses environmental args, for tests you can use custom_new(), more about it here.

// creates module revmod
revparse! {
    [some_arg, 's', "help message"];
    [no_short, "help message"];
    [value, 'v', "help message", "VALUE"];
    [takes_val_no_short, "help message", "SOME"];
}
fn main() {
    let args = revmod::Revparse::new(); 
    // args.some_arg => bool
    // args.no_short => bool
    // args.value => Option<String>
    // args.takes_val_no_short => Option<String>
    println!("{:#?}", args);
}

This would print (if the user entered no arguments):

Revparse {
    some_arg: false,
    no_short: false,
    value: None,
    takes_val_no_short: None,
}

The Arguments that take a value have the type:

Option<String>

If the flag was not given, the value will be None.

Those that don't take a value have the type:

bool

If the flag was not given, the value will be false, if it was, it will be true.

Custom args for testing

You can use the custom_new() function for testing your program with preset arguments. custom_new() takes impl Iterator<Item = String> as a parameter.

So let's test the example above:

revparse! {
    [some_arg, 's', "help message"];
    [no_short, "help message"];
    [value, 'v', "help message", "VALUE"];
    [takes_val_no_short, "help message", "SOME"];
}
// helper function
fn iter_str(args: &[&str]) -> impl Iterator<Item = String> {
    args.iter().map(|i| i.to_string())
}
fn main() {
    let args = revmod::Revparse::custom_new(iter_str(&["exec", "--some-arg", "-vEXAMPLE_VALUE", "--takes-val-no-short", "some_value"]));
    assert_eq!(args.some_arg, true);    // was called
    assert_eq!(args.no_short, false);   // wasn't called
    assert_eq!(args.value.unwrap(), "EXAMPLE_VALUE"); // If -v had not been called, args.value would be `None` and the program would panic.
    assert_eq!(args.takes_val_no_short.unwrap(), "some_value");
    
    // So let's test it with different args
    let args = revmod::Revparse::custom_new(iter_str(&["exec", "-s", "--value=VAL", "--no-short"]));
    assert_eq!(args.some_arg, true); // was called with "-s"
    assert_eq!(args.no_short, true);
    assert_eq!(args.value.unwrap(), "VAL");
    assert_eq!(args.takes_val_no_short, None);  // is None, since it wasn't called
}

Positional Arguments

positional arguments are arguments that do not start with a "-" or "--".

If the user wants to give a positional argument, that does in fact start with a "-", he can write a "--" before the positional argument like this:

program_name -- "----positional argument"

If you don't know what positional arguments are, read this.

There are five settings for positional arguments.

  1. Pos
  2. PosHelp
  3. PosMin
  4. PosMax
  5. PosInfinite

Get Positional Arguments

To get the positional arguments, the user entered you can use the get_pos_args() function.

revparse! {
    [PosMax => 5];
    [PosMin => 1];
}
fn main() {
    let mut args = revmod::Revparse::new();
    let pos_args = args.get_pos_args();
    let len = pos_args.len();
    assert!(len <= 5 && len >= 1); // User has to enter beween 1 and 5 positional arguments.
}

Implementing this for GNU grep

Usage: grep [OPTION]... PATTERNS [FILE]...
Search for PATTERNS in each FILE.
Example: grep -i 'hello world' menu.h main.c
PATTERNS can contain multiple patterns separated by newlines.

To implement this, you would have to use these settings:

[PosHelp => "Search for PATTERNS in each FILE.\nExample: grep -i 'hello world' menu.h main.c\nPATTERNS can contain multiple patterns separated by newlines."];
[Pos => "PATTERNS"];
[Pos => "[FILE]..."];
[ExecName => "grep"];
[PosInfinite => true]; // grep has no limit for the amount of files you can enter.
[PosMin => 1]; // and forces you to enter a Pattern

Settings

The Settings syntax is as follows

[SettingName => ...];

The following Settings exist:

[ExecName => <string literal>];

[Pos => <string literal>];

[PosHelp => <string literal>];

[PosMin => u64];

[PosMax => u64];

[PosInfinite => bool];

[ModName => <identifier>];

ExecName

The name of the executable. Needed for the help message.

Default: program_name

To change it to revparse for example:

[ExecName => "revparse"];

Pos

Setting can be given multiple times. Each Pos setting will be displayed in the "Usage message".

This:

[Pos => "SOME"];
[Pos => "ANOTHER"];

would be displayed like this:

Usage: program_name [OPTION]... SOME ANOTHER

and would raise the default of PosMax to 2, as [Pos => ...]; was given twice.

More

PosHelp

Help message for positional arguments. For example

[PosHelp => "POS HELP MESSAGE"];

would be shown in the help message as:

Usage: program_name [OPTION]...
POS HELP MESSAGE

Options:
...

In case you wonder for what this is, here is an example.

PosMin

The minimum amount of positional arguments the user has to enter.

Default is 0.

To force the user to enter 1 positional argument:

[PosMin => 1];

PosMax

The maximum amount of positional arguments the user has to enter.

Default is the amount of times

[Pos => "SOME"];

was used.

To change it to 5, which would mean, that the user can't enter more than 5 positional arguments:

[PosMax => 5];

This default can be overwritten with either [PosMax => ...]; or [PosInfinite => ...];.

PosInfinite

If this is set to true,

[PosInfinite => true];

there will be no limit, on how much positional arguments the user can enter.

Default is false.

ModName

Name of the module created by the revparse! macro. Default is revmod. If you want to change it to example:

[ModName => example];

example can't be a keyword and should not be written in quotes.

How to use a keyword as a flag

To use a keyword as a flag, use a raw identifier, meaning that you need to add r# before the keyword.

Example with match:

[match, 'm', "help message"]; // compiler error

instead write this:

[r#match, 'm', "help message"]; // compiles

Don't worry, your flag will NOT be --r#match, but will work normally with --match.

Releases

No releases published

Packages

No packages published