blarg_builder/api/
field.rs

1use std::cell::RefCell;
2use std::collections::HashSet;
3use std::marker::PhantomData;
4use std::rc::Rc;
5use std::str::FromStr;
6
7use crate::api::capture::*;
8use crate::model::Nargs;
9use crate::prelude::Collectable;
10
11/// An option parameter that takes a single value (precisely 1).
12pub struct Scalar<'a, T> {
13    variable: Rc<RefCell<&'a mut T>>,
14}
15
16impl<'a, T> CliOption for Scalar<'a, T> {}
17impl<'a, T> CliArgument for Scalar<'a, T> {}
18
19impl<'a, T> Scalar<'a, T> {
20    /// Create a scalar parameter.
21    pub fn new(variable: &'a mut T) -> Self {
22        Self {
23            variable: Rc::new(RefCell::new(variable)),
24        }
25    }
26}
27
28impl<'a, T> GenericCapturable<'a, T> for Scalar<'a, T>
29where
30    T: FromStr,
31{
32    fn matched(&mut self) {
33        // Do nothing.
34    }
35
36    fn capture(&mut self, token: &str) -> Result<(), InvalidCapture> {
37        let result: Result<T, InvalidCapture> =
38            T::from_str(token).map_err(|_| InvalidCapture::InvalidConversion {
39                token: token.to_string(),
40                type_name: std::any::type_name::<T>(),
41            });
42        let value = result?;
43        **self.variable.borrow_mut() = value;
44        Ok(())
45    }
46
47    fn nargs(&self) -> Nargs {
48        Nargs::Precisely(1)
49    }
50}
51
52/// An option parameter that takes no values (precisely 0).
53pub struct Switch<'a, T> {
54    variable: Rc<RefCell<&'a mut T>>,
55    target: Option<T>,
56}
57
58impl<'a, T> CliOption for Switch<'a, T> {}
59
60impl<'a, T> Switch<'a, T> {
61    /// Create a switch parameter.
62    pub fn new(variable: &'a mut T, target: T) -> Self {
63        Self {
64            variable: Rc::new(RefCell::new(variable)),
65            target: Some(target),
66        }
67    }
68}
69
70impl<'a, T> GenericCapturable<'a, T> for Switch<'a, T> {
71    fn matched(&mut self) {
72        **self.variable.borrow_mut() = self
73            .target
74            .take()
75            .expect("internal error - must be able to take the Switch#target");
76    }
77
78    fn capture(&mut self, _token: &str) -> Result<(), InvalidCapture> {
79        unreachable!("internal error - must not capture on a Switch");
80    }
81
82    fn nargs(&self) -> Nargs {
83        Nargs::Precisely(0)
84    }
85}
86
87/// An option parameter that maps down to [`Option`], taking a single value (precisely 1).
88pub struct Optional<'a, T> {
89    variable: Rc<RefCell<&'a mut Option<T>>>,
90}
91
92impl<'a, T> CliOption for Optional<'a, T> {}
93
94impl<'a, T> Optional<'a, T> {
95    /// Create an optional parameter.
96    pub fn new(variable: &'a mut Option<T>) -> Self {
97        Self {
98            variable: Rc::new(RefCell::new(variable)),
99        }
100    }
101}
102
103impl<'a, T> GenericCapturable<'a, T> for Optional<'a, T>
104where
105    T: FromStr,
106{
107    fn matched(&mut self) {
108        // Do nothing
109    }
110
111    fn capture(&mut self, token: &str) -> Result<(), InvalidCapture> {
112        let result: Result<T, InvalidCapture> =
113            T::from_str(token).map_err(|_| InvalidCapture::InvalidConversion {
114                token: token.to_string(),
115                type_name: std::any::type_name::<T>(),
116            });
117        let value = result?;
118        self.variable.borrow_mut().replace(value);
119        Ok(())
120    }
121
122    fn nargs(&self) -> Nargs {
123        Nargs::Precisely(1)
124    }
125}
126
127/// A parameter that takes multiple values (specifiable [`Nargs`]).
128pub struct Collection<'a, C, T>
129where
130    C: 'a + Collectable<T>,
131{
132    variable: Rc<RefCell<&'a mut C>>,
133    nargs: Nargs,
134    _phantom: PhantomData<T>,
135}
136
137impl<'a, C, T> CliOption for Collection<'a, C, T> where C: 'a + Collectable<T> {}
138
139impl<'a, C, T> CliArgument for Collection<'a, C, T> where C: 'a + Collectable<T> {}
140
141impl<'a, C, T> Collection<'a, C, T>
142where
143    C: 'a + Collectable<T>,
144{
145    /// Create a collection parameter.
146    pub fn new(variable: &'a mut C, nargs: Nargs) -> Self {
147        Self {
148            variable: Rc::new(RefCell::new(variable)),
149            nargs,
150            _phantom: PhantomData,
151        }
152    }
153}
154
155impl<'a, C, T> GenericCapturable<'a, T> for Collection<'a, C, T>
156where
157    T: FromStr,
158    C: 'a + Collectable<T>,
159{
160    fn matched(&mut self) {
161        // Do nothing.
162    }
163
164    fn capture(&mut self, token: &str) -> Result<(), InvalidCapture> {
165        let result: Result<T, InvalidCapture> =
166            T::from_str(token).map_err(|_| InvalidCapture::InvalidConversion {
167                token: token.to_string(),
168                type_name: std::any::type_name::<T>(),
169            });
170        let value = result?;
171        (**self.variable.borrow_mut())
172            .add(value)
173            .map_err(|message| InvalidCapture::InvalidAdd {
174                token: token.to_string(),
175                message,
176            })?;
177        Ok(())
178    }
179
180    fn nargs(&self) -> Nargs {
181        self.nargs
182    }
183}
184
185impl<T> Collectable<T> for Vec<T> {
186    fn add(&mut self, item: T) -> Result<(), String> {
187        self.push(item);
188        Ok(())
189    }
190}
191
192impl<T: Eq + std::hash::Hash> Collectable<T> for HashSet<T> {
193    fn add(&mut self, item: T) -> Result<(), String> {
194        if self.insert(item) {
195            Ok(())
196        } else {
197            Err("set already contains item".to_string())
198        }
199    }
200}
201
202#[cfg(test)]
203mod tests {
204    use super::*;
205
206    #[test]
207    fn vec() {
208        let mut collection: Vec<u32> = Vec::default();
209        collection.add(1).unwrap();
210        collection.add(0).unwrap();
211        assert_eq!(collection, vec![1, 0]);
212    }
213
214    #[test]
215    fn hash_set() {
216        let mut collection: HashSet<u32> = HashSet::default();
217        collection.add(1).unwrap();
218        collection.add(0).unwrap();
219        let message = collection.add(1).unwrap_err();
220        assert_eq!(collection, HashSet::from([1, 0]));
221        assert_eq!(message, "set already contains item".to_string());
222    }
223
224    #[test]
225    fn value_capture() {
226        // Integer
227        let mut variable: u32 = u32::default();
228        let mut value = Scalar::new(&mut variable);
229        value.capture("5").unwrap();
230        assert_eq!(variable, 5);
231
232        // Boolean
233        let mut variable: bool = false;
234        let mut value = Scalar::new(&mut variable);
235        value.capture("true").unwrap();
236        assert!(variable);
237    }
238
239    #[test]
240    #[should_panic]
241    fn switch_capture() {
242        let mut variable: u32 = u32::default();
243        let mut switch = Switch::new(&mut variable, 1);
244        match switch.capture("5") {
245            Ok(_) => {}
246            Err(_) => {}
247        };
248    }
249
250    #[test]
251    fn optional_capture() {
252        // Option<u32>
253        let mut variable: Option<u32> = None;
254        let mut optional = Optional::new(&mut variable);
255        optional.capture("1").unwrap();
256        assert_eq!(variable, Some(1));
257    }
258
259    #[test]
260    fn collection_capture() {
261        // Vec<u32>
262        let mut variable: Vec<u32> = Vec::default();
263        let mut collection = Collection::new(&mut variable, Nargs::Any);
264        collection.capture("1").unwrap();
265        collection.capture("0").unwrap();
266        assert_eq!(variable, vec![1, 0]);
267
268        // HashSet<u32>
269        let mut variable: HashSet<u32> = HashSet::default();
270        let mut collection = Collection::new(&mut variable, Nargs::Any);
271        collection.capture("1").unwrap();
272        collection.capture("0").unwrap();
273        let error = collection.capture("0").unwrap_err();
274        assert_eq!(variable, HashSet::from([0, 1]));
275        assert_matches!(error, InvalidCapture::InvalidAdd { token, message } => {
276            assert_eq!(token, "0".to_string());
277            assert_eq!(message, "set already contains item".to_string());
278        });
279    }
280
281    #[test]
282    fn value_overwritten() {
283        let mut variable: u32 = u32::default();
284        let mut value = Scalar::new(&mut variable);
285        value.capture("5").unwrap();
286        variable = 2;
287        assert_eq!(variable, 2);
288    }
289
290    #[test]
291    fn value_matched() {
292        let mut variable: u32 = u32::default();
293        let mut value = Scalar::new(&mut variable);
294        value.matched();
295        assert_eq!(variable, 0);
296    }
297
298    #[test]
299    fn switch_matched() {
300        let mut variable: u32 = u32::default();
301        let mut switch = Switch::new(&mut variable, 2);
302        switch.matched();
303        assert_eq!(variable, 2);
304    }
305
306    #[test]
307    fn optional_matched() {
308        let mut variable: Option<u32> = None;
309        let mut optional = Optional::new(&mut variable);
310        optional.matched();
311        assert_eq!(variable, None);
312    }
313
314    #[test]
315    fn collection_matched() {
316        let mut variable: Vec<u32> = Vec::default();
317        let mut collection = Collection::new(&mut variable, Nargs::Any);
318        collection.matched();
319        assert_eq!(variable, vec![]);
320    }
321
322    #[test]
323    fn test_nargs() {
324        let mut variable: u32 = u32::default();
325        let value = Scalar::new(&mut variable);
326        assert_eq!(value.nargs(), Nargs::Precisely(1));
327
328        let mut variable: u32 = u32::default();
329        let switch = Switch::new(&mut variable, 2);
330        assert_eq!(switch.nargs(), Nargs::Precisely(0));
331
332        let mut variable: Option<u32> = None;
333        let optional = Optional::new(&mut variable);
334        assert_eq!(optional.nargs(), Nargs::Precisely(1));
335
336        let mut variable: Vec<u32> = Vec::default();
337        let collection = Collection::new(&mut variable, Nargs::Any);
338        assert_eq!(collection.nargs(), Nargs::Any);
339
340        let mut variable: Vec<u32> = Vec::default();
341        let collection = Collection::new(&mut variable, Nargs::AtLeastOne);
342        assert_eq!(collection.nargs(), Nargs::AtLeastOne);
343    }
344}