trivial_argument_parser/argument/
parsable_argument.rs1use super::ArgumentIdentification;
2use std::iter::Peekable;
3pub 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
18pub trait HandleableArgument<'a> {
20 fn handle(
22 &mut self,
23 input_iter: &mut Peekable<&mut std::slice::Iter<'_, String>>,
24 ) -> Result<(), String>;
25 fn is_by_short(&self, name: char) -> bool;
27 fn is_by_long(&self, name: &str) -> bool;
29 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 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 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}