cmdparsing 2.2.5

adds a macro to parse arguments
Documentation
#[macro_export]
macro_rules! helper {
    (exists_or_zero;) => {
        1
    };
    (exists_or_zero;$t:tt) => {
        $t
    };
    (exists;,$then:ty) => {
        $then
    };
    (exists;$t:tt, $then:ty) => {
        Vec<$then>
    };
    (exists_flag;,$then:ty) => {
        Option<$then>
    };
    (exists_flag;$t:tt, $then:ty) => {
        Vec<$then>
    };
    (exists_add;,$var:ident, $type:ty, $d:ident, $db:ident) => {
        $var = <$type>::from($d.get($db).expect("this is an error on line 22, please report this bug to https://github.com/chickencuber/clilib").clone());
        $d.remove($db);
    };
    (exists_add;$t:tt,$var:ident, $type:ty, $d:ident, $db:ident) => {
        for _ in 0..$t {
            $var.push(<$type>::from($d.get($db).expect("this is an error on line 27, please report this bug to https://github.com/chickencuber/clilib").clone()));
            $d.remove($db);
        }
    };
    (exists_add_flag;,$var:ident, $type:ty, $d:ident, $db:expr) => {
        $var = Some(<$type>::from($d.get($db).expect("this is an error on line 32, please report this bug to https://github.com/chickencuber/clilib").clone()));
        $d.remove($db);
    };
    (exists_add_flag;$t:tt,$var:ident, $type:ty, $d:ident, $db:expr) => {
        for _ in 0..$t {
            $var.push(<$type>::from($d.get($db).expect("this is an error on line 37, please report this bug to https://github.com/chickencuber/clilib").clone()));
            $d.remove($db);
        }
    };
    (exists_declare;,$var:ident, $type:ty) => {
        let $var: $type;
    };
    (exists_declare;$t:tt,$var:ident, $type:ty) => {
        let mut $var: Vec<$type> = Vec::new();
    };
    (exists_declare_flag;,$var:ident, $type:ty) => {
        let mut $var: Option<$type> = None;
    };
    (exists_declare_flag;$t:tt,$var:ident, $type:ty) => {
        let mut $var: Vec<$type> = Vec::new();
    };
}

#[macro_export]
macro_rules! cmd {
    ($name:ident; help:$help:expr; :$default:expr; $($fname:expr=> $($str:literal)|*),*$(,)?) => {
        pub struct $name;
        impl $name {
            pub fn run(v: Vec<String>) {
                if v.len() == 0 {
                    $default(v);
                    return;
                }
                let cmd = v.get(0).expect(&format!("{}", $help)).clone();
                if cmd == "help" || cmd == "?" {
                    println!("{}", $help);
                    std::process::exit(0);
                } else
                    $(
                        if $(cmd == $str)||* {
                            $fname(v);
                        } else
                    )* {
                        $default(v);
                    }
            }
        }
    };
    ($name:ident; help:$help:expr; .$cmd:ty; :$default:expr; $($fname:expr=> $($str:literal)|*),*$(,)?) => {
        pub struct $name;
        impl $name {
            pub fn run(mut v: Vec<String>) {
                let mut cmd: Option<String> = None;
                let mut allow_flags = false;

                for i in 0..v.len() {
                    let a = &v[i];
                    if allow_flags {
                        cmd = Some(a.clone());
                        v.remove(i);
                        break;
                    }
                    if a == "--" {
                        allow_flags = true;
                        continue;
                    }
                    if a.starts_with("-") {
                        continue;
                    }
                    cmd = Some(a.clone());
                    v.remove(i);
                    break;
                }
                if let Some(cmd) = cmd {
                    if cmd == "help" || cmd == "?" {
                        println!("{}", $help);
                        std::process::exit(0);
                    } else
                        $(
                            if $(cmd == $str)||* {
                                let args = <$cmd>::from(v.clone());
                                $fname(args);
                            } else
                        )* {
                            v.insert(0, cmd);
                            let args = <$cmd>::from(v.clone());
                            $default(args);
                            }
                } else {
                    let args = <$cmd>::from(v.clone());
                    $default(args);
                }
            }
        }
    };
    ($name:ident; help:$help:expr; $(.$cmd:ty;)? $($fname:expr=> $($str:literal)|*),*$(,)?) => {
        fn _default<T>(_: T) {
            eprintln!("{}", $help);
            std::process::exit(101);
        }
        $crate::cmd!{
            $name:ident;
            help:$help;
            $(.$cmd;)?
            :_default;
            $($fname => $($str)|*),*
        }
    };
    (help:$help:expr; $(.$cmd:ty;)? $(:$default:expr;)? $($fname:expr=> $($str:literal)|*),*$(,)?) => {
        fn main() {
            $crate::cmd!{
                _Main;
                help:$help;
                     $(.$cmd;)?
                         $(:$default;)?
                         $($fname => $($str)|*),*
            }
            _Main::run(std::env::args().skip(1).collect());
        }
    };
}

#[macro_export]
macro_rules! define {
    ($name:ident; help: $help:expr; flags {
        $($fname:ident: $ftype:ty = $($flag:literal)|* $(=> [$fnum:literal])?),*$(,)?
    }; args {
        $($aname:ident: $atype:ty $(=> [$num:literal])?),*$(,)?
    };
    $(rest => $rname:ident: $rtype:ty;)?) => {
        #[derive(Debug, Clone)]
        pub struct $name {
            $(pub $fname: tt_call::tt_if!{
                condition = [{tt_equal::tt_equal}]
                    input = [{ $ftype bool }]
                    true = [{
                        bool
                    }]
                false = [{
                    $crate::helper!(exists_flag;$($fnum)?, $ftype)
                }]
            },)*
            $(
                pub $aname: $crate::helper!(exists;$($num)?, $atype),
            )*
                $(
                    pub $rname: Vec<$rtype>
                )?
        }
        impl $name {
            pub fn from(mut __args: Vec<String>) -> Self{
                let mut __handle_flags = true;
                $(
                    tt_call::tt_if!{
                        condition = [{tt_equal::tt_equal}]
                            input = [{ $ftype bool }]
                            true = [{
                                let mut $fname: bool = false;
                            }]
                        false = [{
                            $crate::helper!(exists_declare_flag;$($fnum)?, $fname, $ftype);
                        }]
                    }
                )*
                    $(
                        $crate::helper!(exists_declare;$($num)?,$aname, $atype);
                    )*
                    $(
                        let mut $rname: Vec<$rtype> = Vec::new();
                    )?
                    if
                        __args.contains(&"-help".to_string()) ||
                        __args.contains(&"--help".to_string()) ||
                        __args.contains(&"-?".to_string()) ||
                        __args.contains(&"--?".to_string())
                    {
                        eprintln!("{}", $help);
                        std::process::exit(0);
                    }
                let mut __i = 0;
                $(
                    if __args.len() == 0 {
                        eprintln!("missing argument: '{}'", stringify!($aname));
                        std::process::exit(101);
                    }
                    while __args.get(__i).expect("there is an error on line 146, please report this bug to https://github.com/chickencuber/clilib").starts_with("-") && __handle_flags {
                        let v = Self::has_args(__args.get(__i).expect("there is an error on line 147, please report this bug to https://github.com/chickencuber/clilib").clone());
                        __args.get_mut(__i).expect("there is an error on line 148, please report this bug to https://github.com/chickencuber/clilib").insert(0, '-');
                        if v.0 {
                            __i += v.1;
                            if __args.get(__i).expect("there is an error on line 151, please report this bug to https://github.com/chickencuber/clilib").starts_with("-") && __handle_flags {
                                eprintln!("the flag requires an argument");
                                std::process::exit(101);
                            }
                        }
                        __i += 1;
                    }

                    $crate::helper!(exists_add; $($num)?, $aname, $atype, __args, __i);
                )*
                    $(
                        while __args.len() - __i > 0 {
                            if __args.get(__i).expect("there is an error on line 163, please report this bug to https://github.com/chickencuber/clilib").starts_with("-") && __handle_flags {
                                if $rname.len() > 0 {
                                    break;
                                }
                                while __args.get(__i).unwrap_or(&String::new()).starts_with("-") && __handle_flags {
                                    let v = Self::has_args(__args.get(__i).expect("there is an error on line 168, please report this bug to https://github.com/chickencuber/clilib").clone());
                                    if __args[__i] == "--" {
                                        __handle_flags = false;
                                        __args.remove(__i);
                                        break;
                                    }
                                    __args.get_mut(__i).expect("there is an error on line 169, please report this bug to https://github.com/chickencuber/clilib").insert(0, '-');
                                    if v.0 {
                                        __i += v.1;
                                        if __args.get(__i).unwrap_or(&String::from("-")).starts_with("-") && __handle_flags {
                                            eprintln!("the flag requires an argument");
                                            std::process::exit(101);
                                        }
                                    }
                                    __i += 1;
                                }
                            }

                            if let Some(s) = __args.get(__i) {
                                $rname.push(<$rtype>::from(s.clone()));
                                __args.remove(__i);
                            }

                        }
                )?
                    while __args.len() > 0 {
                        let mut __ch = __args.get(0).expect("there is an error on line 186, please report this bug to https://github.com/chickencuber/clilib").clone();
                        if !(__ch.starts_with("-") && __handle_flags) {
                            eprintln!("too many arguments");
                            std::process::exit(101);
                        }
                        __ch.remove(0);
                        if __ch.starts_with("-") && __handle_flags {
                            __ch.remove(0);
                        }
                        $(
                            if $(__ch == $flag)||* {
                                tt_call::tt_if!{
                                    condition = [{tt_equal::tt_equal}]
                                        input = [{ $ftype bool }]
                                        true = [{
                                            $fname = true;
                                        }]
                                    false = [{
                                        if __args[1].starts_with("-") && __handle_flags {
                                            eprintln!("flags requires an argument");
                                            std::process::exit(101);
                                        }
                                        $crate::helper!(exists_add_flag;$($fnum)?, $fname, $ftype, __args, 1);
                                    }]
                                }
                                __args.remove(0);
                                continue;
                            } else
                        )*
                            if (__ch == "-") {
                                __handle_flags = false;
                                __args.remove(0);
                                break
                            }
                            eprintln!("invalid flag {}", __ch);
                        std::process::exit(101);
                    }
                return Self {
                    $($fname,)*
                        $($aname,)*
                        $($rname)?
                }
            }
            fn has_args(v: String) -> (bool, usize) {
                $(
                    if $(v == concat!("-", $flag) || v == concat!("--", $flag))||* {
                        tt_call::tt_if!{
                            condition = [{tt_equal::tt_equal}]
                                input = [{ $ftype bool }]
                                true = [{
                                    return (false, 0);
                                }]
                            false = [{
                                return (true, $crate::helper!(exists_or_zero;$($fnum)?));
                            }]
                        }
                    } else
                )*
                    if(v == "--") {
                        return (false, 0);
                    }
                eprintln!("invalid flag {}", v);
                std::process::exit(101);
            }
        }
    };
}