palex/input.rs
1use crate::part::{InputPart, InputPartLD};
2use crate::TokenKind;
3
4/// The trait for types that can produce tokens from a list of command-line
5/// arguments.
6///
7/// To implement this trait efficiently, accessing the current token and its
8/// [`TokenKind`] should be cheap.
9///
10/// This trait is implemented for [crate::StringInput].
11pub trait Input {
12 /// Returns the current token as string slice and the [`TokenKind`] of the
13 /// current token, or [None] if the input is empty.
14 ///
15 /// This function skips the leading dashes of arguments. If you don't want
16 /// that, use [`Input::current_str_with_leading_dashes()`] instead.
17 fn current(&self) -> Option<(&str, TokenKind)>;
18
19 /// Returns the current token (including the leading dashes) as string
20 /// slice, or [None] if the input is empty.
21 fn current_str_with_leading_dashes(&self) -> Option<&str>;
22
23 /// Bumps the current token by `len` bytes.
24 ///
25 /// Leading dashes are ignored, e.g. bumping the argument `--foo` by one
26 /// byte returns `f`; the rest of the token is `oo`. If you don't want
27 /// this, use [`Input::bump_with_leading_dashes()`] instead.
28 ///
29 /// If the bytes are followed by an equals sign and the current
30 /// [`TokenKind`] is `OneDash`, `TwoDashes` or `AfterOneDash`, the
31 /// equals sign is skipped.
32 ///
33 /// If afterwards the current argument is empty, a new argument is read and
34 /// becomes the "current token"
35 fn bump(&mut self, len: usize) -> &str;
36
37 /// Bumps the current token (including leading dashes) by `len` bytes.
38 ///
39 /// If the bytes are followed by an equals sign and the current
40 /// [`TokenKind`] is `OneDash`, `TwoDashes` or `AfterOneDash`, the
41 /// equals sign is skipped.
42 ///
43 /// If afterwards the current argument is empty, a new argument is read and
44 /// becomes the "current token"
45 fn bump_with_leading_dashes(&mut self, len: usize) -> &str;
46
47 /// Bumps the current argument (including leading dashes) completely.
48 fn bump_argument(&mut self) -> Option<&str>;
49
50 /// Sets the parsing mode. When `true`, all arguments are considered
51 /// positional, i.e. leading dashes are ignored.
52 fn set_ignore_dashes(&mut self, ignore: bool);
53
54 /// Returns the parsing mode. When `true`, all arguments are considered
55 /// positional, i.e. leading dashes are ignored.
56 fn ignore_dashes(&self) -> bool;
57
58 /// Returns `true` if the input is empty. This means that all arguments have
59 /// been fully parsed.
60 fn is_empty(&self) -> bool {
61 self.current().is_none()
62 }
63
64 /// Returns `true` if the input is not empty. This means that all arguments
65 /// have been fully parsed.
66 fn is_not_empty(&self) -> bool {
67 self.current().is_some()
68 }
69
70 /// Returns `true` if a value within the same argument is expected. Or in
71 /// other words, if we just consumed a single-dash flag or an equals sign
72 /// and there are remaining bytes in the same argument.
73 fn can_parse_value_no_whitespace(&self) -> bool {
74 if let Some((_, current)) = self.current() {
75 matches!(current, TokenKind::AfterOneDash | TokenKind::AfterEquals)
76 } else {
77 false
78 }
79 }
80
81 /// Returns `true` if the current token can be parsed as a flag or named
82 /// argument (e.g. `-h`, `--help=config`).
83 fn can_parse_dash_argument(&self) -> bool {
84 if let Some((_, current)) = self.current() {
85 matches!(
86 current,
87 TokenKind::OneDash | TokenKind::TwoDashes | TokenKind::AfterOneDash
88 )
89 } else {
90 false
91 }
92 }
93
94 /// Eat the current token if the argument doesn't start with dashes and
95 /// matches `token` exactly.
96 fn eat_no_dash<'a>(&mut self, token: &'a str) -> Option<&str> {
97 if let Some((s, TokenKind::NoDash)) = self.current() {
98 if token == s {
99 return Some(self.bump(token.len()));
100 }
101 }
102 None
103 }
104
105 /// Eat the current token if the argument starts with a single dash, and the
106 /// current token starts with `token`.
107 ///
108 /// Does not work if the token appears after an equals sign has already been
109 /// parsed.
110 fn eat_one_dash<'a>(&mut self, token: &'a str) -> Option<&str> {
111 if let Some((s, TokenKind::OneDash)) | Some((s, TokenKind::AfterOneDash)) =
112 self.current()
113 {
114 if s.starts_with(token) {
115 return Some(self.bump(token.len()));
116 }
117 }
118 None
119 }
120
121 /// Eat the current token if the argument starts with (at least) two dashes,
122 /// and the current token either matches `token` exactly, or starts with
123 /// `token` followed by an equals sign.
124 ///
125 /// Does not work if the token appears after an equals sign has already been
126 /// parsed.
127 fn eat_two_dashes<'a>(&mut self, token: &'a str) -> Option<&str> {
128 if let Some((s, TokenKind::TwoDashes)) = self.current() {
129 if let Some(rest) = s.strip_prefix(token) {
130 if rest.is_empty() || rest.starts_with('=') {
131 return Some(self.bump(token.len()));
132 }
133 }
134 }
135 None
136 }
137
138 /// Eat the current token if it matches `token` exactly.
139 ///
140 /// This method only works if the current [`TokenKind`] is either `NoDash`,
141 /// `AfterOneDash` or `AfterEquals`.
142 fn eat_value<'a>(&mut self, token: &'a str) -> Option<&str> {
143 if let Some((s, kind)) = self.current() {
144 match kind {
145 TokenKind::TwoDashes | TokenKind::OneDash => return None,
146
147 | TokenKind::NoDash
148 | TokenKind::AfterOneDash
149 | TokenKind::AfterEquals => {
150 if let Some(rest) = s.strip_prefix(token) {
151 if rest.is_empty() {
152 return Some(self.bump(token.len()));
153 }
154 }
155 }
156 }
157 }
158 None
159 }
160
161 /// Eat the current token (including any leading dashes) if it matches
162 /// `token` exactly.
163 fn eat_value_allows_leading_dashes<'a>(&mut self, token: &'a str) -> Option<&str> {
164 if let Some(s) = self.current_str_with_leading_dashes() {
165 if let Some(rest) = s.strip_prefix(token) {
166 if rest.is_empty() {
167 return Some(self.bump_with_leading_dashes(token.len()));
168 }
169 }
170 }
171 None
172 }
173
174 /// If the argument doesn't start with dashes, returns a helper struct for
175 /// obtaining, validating and eating the next token.
176 fn no_dash(&mut self) -> Option<InputPart<'_, Self>>
177 where
178 Self: Sized,
179 {
180 match self.current() {
181 Some((s, TokenKind::NoDash)) => Some(InputPart::new(s.len(), self)),
182 _ => None,
183 }
184 }
185
186 /// If the argument starts with a single dash, returns a helper struct for
187 /// obtaining, validating and eating the next token.
188 fn one_dash(&mut self) -> Option<InputPart<'_, Self>>
189 where
190 Self: Sized,
191 {
192 match self.current() {
193 Some((s, TokenKind::OneDash)) => Some(InputPart::new(s.len(), self)),
194 _ => None,
195 }
196 }
197
198 /// If the argument starts with two (or more) dashes, returns a helper
199 /// struct for obtaining, validating and eating the next token.
200 fn two_dashes(&mut self) -> Option<InputPart<'_, Self>>
201 where
202 Self: Sized,
203 {
204 match self.current() {
205 Some((s, TokenKind::TwoDashes)) => Some(InputPart::new(s.len(), self)),
206 _ => None,
207 }
208 }
209
210 /// Returns a helper struct for obtaining, validating and eating the next
211 /// token. Works only if the current [`TokenKind`] is either `NoDash`,
212 /// `AfterOneDash` or `AfterEquals`.
213 ///
214 /// The value is not allowed to start with a dash, unless the dash is not at
215 /// the start of the current argument.
216 fn value(&mut self) -> Option<InputPart<'_, Self>>
217 where
218 Self: Sized,
219 {
220 match self.current() {
221 | Some((s, TokenKind::NoDash))
222 | Some((s, TokenKind::AfterOneDash))
223 | Some((s, TokenKind::AfterEquals)) => Some(InputPart::new(s.len(), self)),
224 _ => None,
225 }
226 }
227
228 /// Returns a helper struct for obtaining, validating and eating the next
229 /// token. The value is allowed to start with a dash.
230 fn value_allows_leading_dashes(&mut self) -> Option<InputPartLD<'_, Self>>
231 where
232 Self: Sized,
233 {
234 match self.current_str_with_leading_dashes() {
235 Some(s) => Some(InputPartLD::new(s.len(), self)),
236 None => None,
237 }
238 }
239}