trivial_argument_parser/argument/
parsable_argument.rs

1use super::ArgumentIdentification;
2use std::iter::Peekable;
3/**
4 * Structure which defines how given argument should be handled. Allows for automatic parsing and validation.
5 * Mutable borrow to parsable argument definition has to be registered in ArgumentList. Because of that
6 * registered arguments cannot be used while those borrows exist. Either ArgumentList instance has to be dropped
7 * or there are no further usages of it. This method of defining arguments is preferred as oposed to using
8 * the legacy API.
9 */
10pub struct ParsableValueArgument<V> {
11    identification: ArgumentIdentification,
12    handler: Box<
13        dyn Fn(&mut Peekable<&mut std::slice::Iter<'_, String>>, &mut Vec<V>) -> Result<(), String>,
14    >,
15    values: Vec<V>,
16}
17
18/// Unifies how parsable arguments are parsed.
19pub trait HandleableArgument<'a> {
20    /// Handles argument. Gets all needed values from input iterator.
21    fn handle(
22        &mut self,
23        input_iter: &mut Peekable<&mut std::slice::Iter<'_, String>>,
24    ) -> Result<(), String>;
25    /// Check if this argument is identified by specified short name.
26    fn is_by_short(&self, name: char) -> bool;
27    /// Check if this argument is identified by specified long name.
28    fn is_by_long(&self, name: &str) -> bool;
29    /// Get this arguments identification.
30    fn identification(&self) -> &ArgumentIdentification;
31}
32
33impl<V> ParsableValueArgument<V> {
34    pub fn new<C>(identification: ArgumentIdentification, handler: C) -> ParsableValueArgument<V>
35    where
36        C: Fn(&mut Peekable<&mut std::slice::Iter<'_, String>>, &mut Vec<V>) -> Result<(), String>
37            + 'static,
38    {
39        ParsableValueArgument::<V> {
40            identification,
41            handler: Box::new(handler),
42            values: Vec::new(),
43        }
44    }
45
46    pub fn first_value(&self) -> Option<&V> {
47        self.values().get(0)
48    }
49
50    pub fn values(&self) -> &Vec<V> {
51        &self.values
52    }
53}
54
55impl ParsableValueArgument<i64> {
56    fn validate_integer(v: &str) -> Option<String> {
57        let mut chars_iter = v.chars().peekable();
58        if let Some(c) = chars_iter.next() {
59            if (c != '-' || chars_iter.peek().is_none()) && !c.is_digit(10) {
60                return Option::Some(format!("Input is not a number"));
61            }
62        }
63        for c in chars_iter {
64            if !c.is_digit(10) {
65                return Option::Some(format!("Input is not a number"));
66            }
67        }
68        Option::None
69    }
70    /**
71     * Default integer type argument value handler. Checks whether value contains only digits or starts with minus sign.
72     */
73    pub fn new_integer(identification: ArgumentIdentification) -> ParsableValueArgument<i64> {
74        let handler = |input_iter: &mut Peekable<&mut std::slice::Iter<'_, String>>,
75                       values: &mut Vec<i64>| {
76            if let Option::Some(v) = input_iter.next() {
77                let validation = ParsableValueArgument::validate_integer(v);
78                if let Option::Some(err) = validation {
79                    return Result::Err(err);
80                }
81                match v.parse() {
82                    Result::Ok(v) => {
83                        values.push(v);
84                        Ok(())
85                    }
86                    Result::Err(err) => Result::Err(format!("{}", err)),
87                }
88            } else {
89                Result::Err(String::from("No remaining input values."))
90            }
91        };
92        ParsableValueArgument::new(identification, handler)
93    }
94}
95
96impl ParsableValueArgument<String> {
97    /**
98     * Default string type argument value handler.
99     */
100    pub fn new_string(identification: ArgumentIdentification) -> ParsableValueArgument<String> {
101        let handler = |input_iter: &mut Peekable<&mut std::slice::Iter<'_, String>>,
102                       values: &mut Vec<String>| {
103            if let Some(v) = input_iter.next() {
104                values.push(String::from(v));
105                Result::Ok(())
106            } else {
107                Result::Err(String::from("No remaining input values."))
108            }
109        };
110        ParsableValueArgument::new(identification, handler)
111    }
112}
113
114impl<'a, V> HandleableArgument<'a> for ParsableValueArgument<V> {
115    fn handle(
116        &mut self,
117        input_iter: &mut Peekable<&mut std::slice::Iter<'_, String>>,
118    ) -> Result<(), String> {
119        (self.handler)(input_iter, &mut self.values)?;
120        Result::Ok(())
121    }
122
123    fn is_by_short(&self, name: char) -> bool {
124        self.identification().is_by_short(name)
125    }
126
127    fn is_by_long(&self, name: &str) -> bool {
128        self.identification().is_by_long(name)
129    }
130
131    fn identification(&self) -> &ArgumentIdentification {
132        &self.identification
133    }
134}
135
136#[cfg(test)]
137mod test {
138    use std::borrow::BorrowMut;
139
140    use super::{HandleableArgument, ParsableValueArgument};
141
142    #[test]
143    fn new_parsable_value_argument_works() {
144        let _arg =
145            ParsableValueArgument::<i64>::new(super::ArgumentIdentification::Short('x'), |_, _| {
146                Result::Ok(())
147            });
148    }
149
150    #[test]
151    fn is_by_short_works() {
152        let arg =
153            ParsableValueArgument::<i64>::new(super::ArgumentIdentification::Short('x'), |_, _| {
154                Result::Ok(())
155            });
156        assert!(arg.is_by_short('x'));
157        assert!(!arg.is_by_short('c'));
158    }
159
160    #[test]
161    fn is_by_long_works() {
162        let arg = ParsableValueArgument::<i64>::new(
163            super::ArgumentIdentification::Long(String::from("path")),
164            |_, _| Result::Ok(()),
165        );
166        assert!(arg.is_by_long("path"));
167        assert!(!arg.is_by_long("directory"));
168    }
169
170    #[test]
171    fn basic_integer_argument_works() {
172        let mut arg =
173            ParsableValueArgument::<i64>::new_integer(super::ArgumentIdentification::Short('i'));
174        assert!(arg
175            .handle(&mut vec![String::from("123")].iter().borrow_mut().peekable())
176            .is_ok());
177        assert_eq!(arg.values.get(0).unwrap(), &123);
178        assert!(arg
179            .handle(&mut vec![String::from("333")].iter().borrow_mut().peekable())
180            .is_ok());
181        assert_eq!(2, arg.values.len());
182        assert_eq!(arg.values.get(0).unwrap(), &123);
183        assert_eq!(arg.values.get(1).unwrap(), &333);
184        assert!(arg
185            .handle(&mut vec![String::from("-333")].iter().borrow_mut().peekable())
186            .is_ok());
187    }
188
189    #[test]
190    fn basic_integer_argument_handler_fails_invalid_number() {
191        let mut arg =
192            ParsableValueArgument::<i64>::new_integer(super::ArgumentIdentification::Short('i'));
193        assert!(arg
194            .handle(&mut vec![String::from("-")].iter().borrow_mut().peekable())
195            .is_err());
196        assert!(arg
197            .handle(&mut vec![String::from("12a")].iter().borrow_mut().peekable())
198            .is_err());
199        assert!(arg
200            .handle(&mut vec![String::from("123.12")].iter().borrow_mut().peekable())
201            .is_err());
202    }
203
204    #[test]
205    fn first_value_works() {
206        let mut arg = ParsableValueArgument::new_integer(super::ArgumentIdentification::Short('i'));
207        assert!(arg.first_value().is_none());
208        assert!(arg
209            .handle(&mut vec![String::from("123")].iter().borrow_mut().peekable())
210            .is_ok());
211        assert_eq!(arg.first_value().unwrap(), &123);
212    }
213}