parkour/
parse.rs

1use palex::Input;
2
3use crate::{Error, ErrorInner, FromInput, FromInputValue};
4
5/// An extension trait of [`palex::Input`], the trait for types that can produce
6/// tokens from a list of command-line arguments.
7///
8/// This trait provides several convenience methods for parsing different
9/// things.
10///
11/// Note that this trait is automatically implemented for all types that
12/// implement `Input`.
13pub trait Parse: Input + Sized {
14    /// Parse something using the [`FromInput`] trait
15    #[inline]
16    fn parse<F: FromInput>(&mut self, context: &F::Context) -> Result<F, Error> {
17        F::from_input(self, context)
18    }
19
20    /// Parse something using the [`FromInput`] trait, but convert
21    /// [`Error::no_value`] to [`Option::None`]. This is useful when you want to
22    /// bubble up all errors except for [`Error::no_value`]:
23    ///
24    /// ```no_run
25    /// # use parkour::prelude::*;
26    /// # let input: parkour::StringInput = todo!();
27    /// if let Some(x) = input.try_parse(&Flag::Short("o").into())? {
28    ///     // do something with x
29    /// #  let _: usize = x;
30    /// }
31    /// # Ok::<(), parkour::Error>(())
32    /// ```
33    #[inline]
34    fn try_parse<F: FromInput>(
35        &mut self,
36        context: &F::Context,
37    ) -> Result<Option<F>, Error> {
38        F::try_from_input(self, context)
39    }
40
41    /// Parse a _value_ using the [`FromInputValue`] trait.
42    fn parse_value<V: FromInputValue>(
43        &mut self,
44        context: &V::Context,
45    ) -> Result<V, Error>;
46
47    /// Parse a _value_ using the [`FromInputValue`] trait, but convert
48    /// [`Error::no_value`] to [`Option::None`]. This is useful when you want to
49    /// bubble up all errors except for [`Error::no_value`]:
50    ///
51    /// ```no_run
52    /// # use parkour::prelude::*;
53    /// # let input: parkour::StringInput = todo!();
54    /// if let Some(value) = input.try_parse_value(&Default::default())? {
55    ///     // do something with value
56    /// #  let _: usize = value;
57    /// }
58    /// # Ok::<(), parkour::Error>(())
59    /// ```
60    #[inline]
61    fn try_parse_value<V: FromInputValue>(
62        &mut self,
63        context: &V::Context,
64    ) -> Result<Option<V>, Error> {
65        match self.parse_value(context) {
66            Ok(value) => Ok(Some(value)),
67            Err(e) if e.is_no_value() => Ok(None),
68            Err(e) => Err(e),
69        }
70    }
71
72    /// Convenience function for parsing a flag with a single dash, like `-h` or
73    /// `-foo`. Returns `true` if it succeeded.
74    fn parse_short_flag(&mut self, flag: &str) -> bool;
75
76    /// Convenience function for parsing a flag with two dashes, like `--h` or
77    /// `--foo`. Returns `true` if it succeeded.
78    fn parse_long_flag(&mut self, flag: &str) -> bool;
79
80    /// Convenience function for parsing a (sub)command, i.e. an argument that
81    /// doesn't start with a dash. Returns `true` if it succeeded.
82    fn parse_command(&mut self, command: &str) -> bool;
83
84    /// Returns an error if the input is not yet empty.
85    fn expect_empty(&mut self) -> Result<(), Error> {
86        if !self.is_empty() {
87            return Err(ErrorInner::UnexpectedArgument {
88                arg: self.bump_argument().unwrap().to_string(),
89            }
90            .into());
91        }
92        Ok(())
93    }
94}
95
96impl<I: Input> Parse for I {
97    #[inline]
98    fn parse_value<V: FromInputValue>(
99        &mut self,
100        context: &V::Context,
101    ) -> Result<V, Error> {
102        if V::allow_leading_dashes(&context) {
103            let value = self.value_allows_leading_dashes().ok_or_else(Error::no_value)?;
104            let result = V::from_input_value(value.as_str(), context)?;
105            value.eat();
106            Ok(result)
107        } else {
108            let value = self.value().ok_or_else(Error::no_value)?;
109            let result = V::from_input_value(value.as_str(), context)?;
110            value.eat();
111            Ok(result)
112        }
113    }
114
115    #[inline]
116    fn parse_short_flag(&mut self, flag: &str) -> bool {
117        self.eat_one_dash(flag).is_some()
118    }
119
120    #[inline]
121    fn parse_long_flag(&mut self, flag: &str) -> bool {
122        self.eat_two_dashes(flag).is_some()
123    }
124
125    #[inline]
126    fn parse_command(&mut self, command: &str) -> bool {
127        self.eat_no_dash(command).is_some()
128    }
129}