Macro tiny_cli::arg_parse

source ·
macro_rules! arg_parse {
    (
        #[name($name:expr)]
        #[description($app_desc:expr)]
        $(#[$outer:meta])*
        $vis:vis struct $ArgStruct:ident {
            $(
                #[short($short_desc:expr), long($long_desc:expr), description($desc:expr)]
                $required_field:ident: $req_ty:ty,
            )*
            $(
                #[optional]
                #[short($opt_short_desc:expr), long($opt_long_desc:expr), description($opt_desc:expr)]
                $optional_field:ident: Option<$opt_ty:ty>,
            )*
            $(
                #[repeating]
                #[short($rep_short_desc:expr), long($rep_long_desc:expr), description($rep_desc:expr)]
                $repeating_field:ident: Vec<$rep_ty:ty>,
            )*
            $(
                #[subcommand($sc_name:expr)]
                $subcommand:ident: Option<$sc_ty:ty>,
            )*
        }
    ) => { ... };
}
Expand description

A macro that parses args. The goal is not to be a beautiful full-featured arg-parser, there are many projects which does that better. This is only supposed to supposed to support the minimum to create a CLI that takes arguments. There are Four kinds of arguments that are accepted:

  1. Required arguments, passed as --long or -s (short) followed by argument.
  2. Optional arguments, passed as --long or -s (short) followed by argument, or not at all.
  3. Repeating arguments, passed 0 or more times as --long or -s (short) followed by argument.
  4. Subcommands, passed as command-name. I have a special distaste for proc_macros, regular macros are limited but can at least be expanded with relative ease, but it does come with limitations. One of those being the above fields having to be declared in order. Required always first, subcommands always last.

Some more requirements:

  1. Repeating arguments are always Vec<T>.
  2. Optional arguments are always Option<T>.
  3. Subcommands are always Option<T>
  4. arg_parse!-structs need a #[name("")]-section, followed by a #[description("")]-section
  5. All fields except subcommands need a #[short(""), long(""), description("")] right above it.
  6. Optional fields require a #[optional] above the required 5.
  7. Repeating arguments need a #[repeating] above the required 5.
  8. Subcommands need a #[subcommand("")] right above it

Ex:

extern crate alloc;
use tiny_cli::arg_parse;
arg_parse!(
    #[name("My nested command")]
    #[description("Use this nested subcommand for fun and profit")]
    pub struct NestedSc {
        #[short("f"), long("field-1"), description("My first field")]
        my_sc_field: String,
        #[short("s"), long("sc-field"), description("My sc second field")]
        my_other_sc_field: i64,
    }
);
arg_parse!(
    #[name("My first first level subcommand")]
    #[description("Use this nested subcommand for fun and profit")]
    pub struct ScOne {
        #[short("h"), long("has-field"), description("My first field")]
        my_sc_field: String,
        #[subcommand("nested")]
        nested: Option<NestedSc>,
    }
);
arg_parse!(
    #[name("My second first level subcommand")]
    #[description("Use this nested subcommand for fun and profit")]
    pub struct ScTwo {
        #[optional]
        #[short("h"), long("has-field"), description("My first field")]
        opt_option: Option<String>,
        #[repeating]
        #[short("r"), long("rep-field"), description("My repeating field")]
        rep_option: Vec<i32>,
    }
);

arg_parse!(
    #[name("My top level command")]
    #[description("Use top level command with subcommands for fun and profit")]
    pub struct Mc {
        #[short("m"), long("my-field"), description("My first field")]
        my_field: String,

        #[subcommand("first-sub")]
        subcommand_one: Option<ScOne>,

        #[subcommand("second-sub")]
        subcommand_two: Option<ScTwo>,
    }
);