1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
/// Command line argument flag for on/off state.
///
/// Uses `std::env:args()` to determine the arguments passed to the program.
/// If there is an argument matching the flag's name then this variable will
/// be truthy. There are two flag types that are often passed in cli arguments:
/// *short-name form* (-f) and *long-name form* (--flag). This macro replaces
/// underscores in long-name argument token with hyphens (e.g.:
/// `flag!(--my_boolean)` -> `"--my-boolean"`).
///
/// ```
/// use ezcli::flag;
/// // args: "-b --my-boolean"
///
/// // accepts "-b" (short-name form)
/// // if passed in, this expression is true
/// flag!(-b);
/// // accepts "--my_boolean" (long-name form)
/// // if passed in, this expression is true
/// flag!(--my_boolean);
/// ```
/// ------
/// Use the keyword `let` before the argument to create a variable with the same
/// name as the argument.
/// ```
/// use ezcli::flag;
///
/// // Creates a variable named after the flag
/// flag!(let --my_boolean);
/// ```
/// ------
/// You can use both *short-name* (-f) and *long-name forms* (--flag) together
/// by separating them with a comma.
/// ```
/// use ezcli::flag;
///
/// flag!(-b, --my_boolean);
/// let bool_var = flag!(-b, --my_boolean);
/// ```
/// ------
/// In some case of not wanting to use the program's environment arguments
/// using a slice is also possible.
/// ```
/// use ezcli::flag;
///
/// let args = ["--my_boolean"];
/// flag!(--my_boolean, args);
/// ```
/// ------
///  Flag groups can be used with short-name flags
/// ```
/// use ezcli::flag;
///
/// let args = ["-abc"];
/// flag!(-a, args);
/// flag!(-b, args);
/// ```
#[macro_export]
macro_rules! flag {
    (-$short_name:tt) => {
        $crate::flag::has_short_name_flag(std::env::args().into_iter(), stringify!($short_name))
    };
    (--$long_name:tt) => {
        $crate::flag::has_long_name_flag(std::env::args().into_iter(), stringify!($long_name))
    };

    (let -$short_name:tt) => {
        let $short_name: bool = $crate::flag!(-$short_name);
    };
    (let --$long_name:tt) => {
        let $long_name: bool = $crate::flag!(--$long_name);
    };
    (-$short_name:tt, --$long_name:tt) => {
        $crate::flag!(-$short_name) || $crate::flag!(--$long_name)
    };

    (-$short_name:tt, $args:ident) => {
        $crate::flag::has_short_name_flag(
            (&$args).into_iter().map(|a| a.to_string()),
            stringify!($short_name),
        )
    };
    (--$long_name:tt, $args:ident) => {
        $crate::flag::has_long_name_flag(
            (&$args).into_iter().map(|a| a.to_string()),
            stringify!($long_name),
        )
    };
    (let -$short_name:tt, $args:ident) => {
        let $short_name: bool = $crate::flag!(-$short_name, $args);
    };
    (let --$long_name:tt, $args:ident) => {
        let $long_name: bool = $crate::flag!(--$long_name, $args);
    };
    (-$short_name:tt, --$long_name:tt, $args:ident) => {
        $crate::flag!(-$short_name, $args) || $crate::flag!(--$long_name, $args)
    };
}

/// Check if the short-name version of the flag is present in the args.
pub fn has_short_name_flag(args: impl Iterator<Item = String>, name: &str) -> bool {
    args.skip(1)
        .find(|s| {
            s.chars().nth(0) == Some('-') && s.chars().nth(1) != Some('-') && s.contains(&name)
        })
        .is_some()
}

/// Check if the long-name version of the flag is present in the args.
pub fn has_long_name_flag(args: impl Iterator<Item = String>, name: &str) -> bool {
    args.skip(1)
        .find(|s| **s == format!("--{}", name.replace('_', "-")))
        .is_some()
}