clip/
arg.rs

1pub const NUM_VALUES: usize = 16;
2pub type Values<'a> = [&'a str; NUM_VALUES];
3
4
5#[derive(Clone, Copy, PartialEq, Eq, Debug)]
6enum NumArgs {
7    None,
8    All,
9    Number(u32)
10}
11
12
13#[derive(Clone, Copy, PartialEq, Eq, Debug)]
14enum ArgName<'a> {
15    Name(&'a str),
16    Flag(&'a str)
17}
18
19
20#[derive(Clone, Copy, PartialEq, Eq, Debug)]
21pub struct Arg<'a> {
22    name: ArgName<'a>,
23    num_args: NumArgs,
24    matched_data: Values<'a>
25}
26
27
28/// Convert slice of strs to args
29pub fn args<'a>(a: &[&'a str]) -> Values<'a> {
30    let mut result = [""; NUM_VALUES];
31    for (i, arg) in a.clone().iter().enumerate() {
32        result[i] = arg.clone();
33    }
34    result
35}
36
37
38fn to_argname<'a>(name: &'a str) -> ArgName<'a> {
39    if let Some(ch) = name.chars().nth(0) {
40        match ch {
41            '-' => {
42                ArgName::Flag(name)
43            },
44            _ => {
45                ArgName::Name(name)
46            }
47        }
48    } else {
49        ArgName::Name(name)
50    }
51}
52
53impl<'a> Arg<'a> {
54    pub fn new(name: &'a str) -> Self {
55        Self {
56            name: to_argname(name),
57            num_args: NumArgs::Number(1),
58            matched_data: [""; NUM_VALUES]
59        }
60    }
61
62    /// Is this string this argument's name?
63    pub fn has_name(&self, name: &'a str) -> bool {
64        to_argname(name) == self.name
65    }
66
67    /// Is this argument a flag or a name?
68    pub fn is_flag(&self) -> bool {
69        match self.name {
70            ArgName::Name(_) => false,
71            ArgName::Flag(_) => true
72        }
73    }
74
75    /// Set the number of values this argument accepts IF its a flag!
76    pub fn num_values(mut self, n: i32) -> Self {
77        if let ArgName::Flag(_) = self.name {
78            self.num_args = match n {
79                -1 => NumArgs::All,
80                0 => NumArgs::None,
81                n => NumArgs::Number(n as u32),
82            };
83        }
84
85        self
86    }
87
88    pub fn get_values(&self) -> Values<'a> {
89        self.matched_data
90    }
91
92    /// Test if this Arg could consume a list of values
93    /// If this arg has already consumed arguments, it wont match
94    /// A name must accept exactly one argument
95    pub fn is_match(&self, slice_matches: &[&'a str]) -> bool {
96        // Confirm we are an argument
97        if self.has_name("") { return false; }
98
99        // Convert slice to array that we can mess with
100        let matches = args(slice_matches);
101
102        // Confirm we havent already consumed data
103        if self.matched_data[0] != "" { return false; }
104
105        // Get the number of args passed in the slice
106        let mut num_args = 0;
107        for m in matches.iter() {
108            if m.clone() != "" { num_args += 1; }
109        }
110
111        // Match the input data based on whether 
112        // or not this instance is a flag or a name
113        match self.name {
114            ArgName::Name(_) => {
115                let valid_num_args = match self.num_args {
116                    // A name can only have one argument
117                    NumArgs::Number(1) => num_args == 1,
118                    // A name cant have more than one argument, or none arguments
119                    NumArgs::All | NumArgs::Number(_) | NumArgs::None => false,
120                };
121
122                valid_num_args
123            },
124            ArgName::Flag(n) => {
125                let valid_num_args = match self.num_args {
126                    NumArgs::None => num_args == 1,
127                    NumArgs::All => true,
128                    NumArgs::Number(n) => num_args == n + 1
129                };
130
131                valid_num_args && (matches[0] == n)
132            }
133        }
134    }
135
136    /// Takes an array of values and consumes data from them
137    /// Returns true if did consume, returns false if did not consume
138    pub fn consume<'b>(&mut self, values: &'b mut Values<'a>) -> bool {
139        let mut matched_up_to = 0;
140        let mut consumed = false;
141
142        for n in (1..values.len()).rev() {
143            if (*values)[n-1] == "" {
144                continue;
145            }
146
147            if self.is_match(&values[0..n]) {
148
149                if self.is_flag() {
150                    self.matched_data = args(&values[1..n]);
151                } else {
152                    self.matched_data = args(&values[0..n]);
153                }
154                matched_up_to = n;
155
156                consumed = true;
157            }
158        }
159
160        *values = args(&values[matched_up_to..]);
161        consumed
162    }
163}