1use winnow::combinator::{alt, repeat, trace};
37use winnow::error::{ModalError as _, ParserError as _};
38use winnow::prelude::*;
39
40use crate::error::Error;
41use crate::stream::Argv;
42use crate::token::{self, Arg, ValueOptions};
43use crate::value::FromArg;
44use winnow::stream::BStr;
45
46#[derive(Copy, Clone, Debug, PartialEq, Eq)]
53pub struct Named<const N: usize = 1> {
54 short: Option<char>,
55 longs: [Option<&'static str>; N],
56 options: ValueOptions,
58}
59
60pub const fn short(c: char) -> Named {
62 Named {
63 short: Some(c),
64 longs: [None],
65 options: ValueOptions::DEFAULT,
66 }
67}
68
69pub const fn long(name: &'static str) -> Named {
71 Named {
72 short: None,
73 longs: [Some(name)],
74 options: ValueOptions::DEFAULT,
75 }
76}
77
78impl<const N: usize> Named<N> {
79 pub const fn short(mut self, c: char) -> Self {
81 self.short = Some(c);
82 self
83 }
84
85 pub const fn long(self, name: &'static str) -> Named {
87 Named {
88 short: self.short,
89 longs: [Some(name)],
90 options: self.options,
91 }
92 }
93
94 pub const fn longs<const M: usize>(self, names: [&'static str; M]) -> Named<M> {
97 let mut longs = [None; M];
98 let mut i = 0;
99 while i < M {
100 longs[i] = Some(names[i]);
101 i += 1;
102 }
103 Named {
104 short: self.short,
105 longs,
106 options: self.options,
107 }
108 }
109
110 pub const fn allow_hyphen_values(mut self) -> Self {
112 self.options.hyphen_values = true;
113 self
114 }
115
116 pub fn negated_by<'i, const M: usize>(
119 self,
120 no: Named<M>,
121 ) -> impl Parser<Argv<'i>, bool, Error> {
122 trace(
123 "negated_by",
124 alt((self.switch().value(true), no.switch().value(false))),
125 )
126 }
127
128 pub const fn require_equals(mut self) -> Self {
130 self.options.require_equals = true;
131 self
132 }
133
134 pub const fn allow_negative_numbers(mut self) -> Self {
136 self.options.negative_numbers = true;
137 self
138 }
139
140 #[inline]
142 pub fn matches(&self, arg: &Arg<'_>) -> bool {
143 match arg {
144 Arg::Long(f) => self
145 .longs
146 .iter()
147 .any(|l| l.is_some_and(|l| f.name == l.as_bytes())),
148 Arg::Short(f) => self.short == Some(f.letter),
149 Arg::Word(_) | Arg::Separator { .. } => false,
150 }
151 }
152
153 pub fn display(&self) -> String {
155 match (self.longs.first().copied().flatten(), self.short) {
156 (Some(l), _) => format!("--{l}"),
157 (None, Some(c)) => format!("-{c}"),
158 (None, None) => String::new(),
159 }
160 }
161
162 pub fn missing(&self, input: &Argv<'_>) -> Error {
164 Error::missing_required(input.offset(), self.display())
165 }
166
167 pub fn switch<'i>(mut self) -> impl Parser<Argv<'i>, (), Error> {
169 trace("switch", move |input: &mut Argv<'i>| {
170 self.parse_next(input)?.switch().parse_next(input)
171 })
172 }
173
174 pub fn argument<'i>(mut self) -> impl Parser<Argv<'i>, &'i BStr, Error> {
176 trace("argument", move |input: &mut Argv<'i>| {
177 let arg = self.parse_next(input)?;
178 arg.read_value_with(input, self.options)
179 })
180 }
181
182 pub fn argument_as<'i, T: FromArg>(mut self) -> impl Parser<Argv<'i>, T, Error> {
184 trace("argument_as", move |input: &mut Argv<'i>| {
185 self.parse_next(input)?
186 .value_as_with(self.options)
187 .parse_next(input)
188 })
189 }
190
191 pub fn argument_or<'i, T: FromArg>(
194 mut self,
195 missing: &'static str,
196 ) -> impl Parser<Argv<'i>, T, Error> {
197 trace("argument_or", move |input: &mut Argv<'i>| {
198 let arg = self.parse_next(input)?;
199 let value = arg.read_value_or_with(input, self.options, BStr::new(missing));
200 arg.convert(value)
201 })
202 }
203
204 pub fn argument_opt<'i>(mut self) -> impl Parser<Argv<'i>, Option<&'i BStr>, Error> {
207 trace("argument_opt", move |input: &mut Argv<'i>| {
208 let arg = self.parse_next(input)?;
209 Ok(arg.read_value_opt_with(input, self.options))
210 })
211 }
212
213 pub fn argument_opt_as<'i, T: FromArg>(mut self) -> impl Parser<Argv<'i>, Option<T>, Error> {
215 trace("argument_opt_as", move |input: &mut Argv<'i>| {
216 let arg = self.parse_next(input)?;
217 arg.read_value_opt_with(input, self.options)
218 .map(|v| arg.convert(v))
219 .transpose()
220 })
221 }
222
223 pub fn arguments_as<'i, T: FromArg>(
226 mut self,
227 delimiter: u8,
228 ) -> impl Parser<Argv<'i>, Vec<T>, Error> {
229 trace("arguments_as", move |input: &mut Argv<'i>| {
230 let arg = self.parse_next(input)?;
231 let value = arg.read_value_with(input, self.options)?;
232 token::split(value, delimiter)
233 .map(|v| arg.convert(v))
234 .collect()
235 })
236 }
237}
238
239impl<'i, const N: usize> Parser<Argv<'i>, Arg<'i>, Error> for Named<N> {
241 #[inline]
242 fn parse_next(&mut self, input: &mut Argv<'i>) -> Result<Arg<'i>, Error> {
243 token::arg
244 .verify(|arg: &Arg<'i>| self.matches(arg))
245 .parse_next(input)
246 }
247}
248
249pub fn positional<'i, T: FromArg>(name: &'static str) -> impl Parser<Argv<'i>, T, Error> {
255 trace("positional", move |input: &mut Argv<'i>| {
256 token::word(input)?.value_as(name).parse_next(input)
257 })
258}
259
260pub trait Names {
262 fn contains(&self, word: &[u8]) -> bool;
264}
265
266impl Names for &str {
267 #[inline]
268 fn contains(&self, word: &[u8]) -> bool {
269 self.as_bytes() == word
270 }
271}
272
273impl<const N: usize> Names for [&str; N] {
274 #[inline]
275 fn contains(&self, word: &[u8]) -> bool {
276 self.iter().any(|name| name.as_bytes() == word)
277 }
278}
279
280pub fn command<'i, O, P>(name: impl Names, mut inner: P) -> impl Parser<Argv<'i>, O, Error>
286where
287 P: Parser<Argv<'i>, O, Error>,
288{
289 trace("command", move |input: &mut Argv<'i>| {
290 let start = input.checkpoint();
291 match token::word(input) {
292 Ok(word) if name.contains(word.value) && !word.after_separator => {
293 inner.parse_next(input).map_err(|e| e.cut())
294 }
295 Ok(_) => {
296 input.reset(&start);
297 Err(Error::from_input(input))
298 }
299 Err(e) => Err(e),
300 }
301 })
302}
303
304pub fn args<'i, P>(mut item: P) -> impl Parser<Argv<'i>, (), Error>
311where
312 P: Parser<Argv<'i>, (), Error>,
313{
314 trace("args", move |input: &mut Argv<'i>| {
315 let separator = token::arg
316 .verify(|a: &Arg<'i>| matches!(a, Arg::Separator { .. }))
317 .void();
318 repeat::<_, _, (), _, _>(0.., winnow::combinator::alt((item.by_ref(), separator)))
319 .parse_next(input)?;
320 token::finish(input).map_err(|e| e.cut())
321 })
322}