duat_core/cmd/
parameters.rs

1//! Defines the processing of parameters in commands
2//!
3//! This processing first separates the [`Flags`] and [`Args`] of the
4//! call, and then transforms the list of arguments into a list of
5//! [`Parameter`]s, as defined by the command. Each [`Parameter`] may
6//! take multiple words, which makes this structure very flexible for
7//! multiple branching paths on how to read the arguments, all from
8//! the same command.
9use std::{iter::Peekable, ops::Range, path::PathBuf};
10
11use crossterm::style::Color;
12
13use crate::{
14    context::Handle,
15    data::Pass,
16    form::{self, FormId},
17    text::{Text, txt},
18};
19
20macro_rules! implDeref {
21    ($type:ty, $target:ty $(, $($args:tt)+)?) => {
22        impl$(<$($args)+>)? std::ops::Deref for $type$(<$($args)+>)? {
23            type Target = $target;
24
25            fn deref(&self) -> &Self::Target {
26                &self.0
27            }
28        }
29
30        impl$(<$($args)+>)? std::ops::DerefMut for $type$(<$($args)+>)? {
31            fn deref_mut(&mut self) -> &mut Self::Target {
32                &mut self.0
33            }
34        }
35    }
36}
37
38/// A parameter for commands that can be called
39///
40/// This parameter must be parseable from [`Args`], which come from a
41/// `&str`. It can take multiple words, and can be composed of other
42/// [`Parameter`]s. An example of this is the [`Form`], which is
43/// composed of multiple [`Color`] parameters, which are then composed
44/// of some format (rgb, hsl), which is then composed of more
45/// parameters, like rgb values, for example.
46///
47/// Other types of [`Parameter`] are just a "list" of other
48/// [`Parameter`]s. For example, [`Vec<P>`] can be used as a
49/// [`Parameter`] to capture any number of `P` arguments.
50/// Additionally, there is the [`Between<MIN, MAX, P>`], which is
51/// _like_ [`Vec<P>`], but takes at least `MIN` `P`s and at most `MAX`
52/// `P`s.
53///
54/// [`Form`]: crate::form::Form
55pub trait Parameter: Sized {
56    /// Tries to consume arguments until forming a parameter
57    ///
58    /// Since parameters shouldn't mutate data, pa is just a regular
59    /// shared reference.
60    fn new(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text>;
61}
62
63impl<P: Parameter> Parameter for Option<P> {
64    /// Will match either [`Parameter`] given, or nothing
65    ///
66    /// This, like other lists, _has_ to be the final argument in the
67    /// [`Parameter`] list, as it will either match correcly, finish
68    /// matching, or match incorrectly in order to give accurate
69    /// feedback.
70    fn new(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
71        match args.next_as::<P>(pa) {
72            Ok(arg) => Ok((Some(arg), None)),
73            Err(err) if args.is_forming_param => Err(err),
74            Err(_) => Ok((None, None)),
75        }
76    }
77}
78
79impl<P: Parameter> Parameter for Vec<P> {
80    /// Will match a list of [`Parameter`]s
81    ///
82    /// This, like other lists, _has_ to be the final argument in the
83    /// [`Parameter`] list, as it will either match correcly, finish
84    /// matching, or match incorrectly in order to give accurate
85    /// feedback.
86    fn new(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
87        let mut returns = Vec::new();
88
89        loop {
90            match args.next_as::<P>(pa) {
91                Ok(ret) => returns.push(ret),
92                Err(err) if args.is_forming_param => return Err(err),
93                Err(_) => break Ok((returns, None)),
94            }
95        }
96    }
97}
98
99impl<const N: usize, P: Parameter> Parameter for [P; N] {
100    /// Will match either the argument given, or nothing
101    ///
102    /// This, like other lists, _has_ to be the final argument in the
103    /// [`Parameter`] list, as it will either match correcly, finish
104    /// matching, or match incorrectly in order to give accurate
105    /// feedback.
106    fn new(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
107        use std::mem::MaybeUninit;
108        let mut returns = [const { MaybeUninit::uninit() }; N];
109
110        for r in returns.iter_mut() {
111            match args.next_as::<P>(pa) {
112                Ok(ret) => *r = MaybeUninit::new(ret),
113                Err(err) => return Err(err),
114            }
115        }
116
117        Ok((returns.map(|ret| unsafe { ret.assume_init() }), None))
118    }
119}
120
121/// Command [`Parameter`]: A list of between `MIN` and `MAX` items
122///
123/// This, like other lists, _has_ to be the final argument in the
124/// [`Parameter`] list, as it will either match correcly, finish
125/// matching, or match incorrectly in order to give accurate
126/// feedback.
127pub struct Between<const MIN: usize, const MAX: usize, P>(pub Vec<P>);
128
129impl<const MIN: usize, const MAX: usize, P: Parameter> Parameter for Between<MIN, MAX, P> {
130    /// Will match between `MIN` and `MAX` [`Parameter`]s
131    ///
132    /// This, like other lists, _has_ to be the final argument in the
133    /// [`Parameter`] list, as it will either match correcly, finish
134    /// matching, or match incorrectly in order to give accurate
135    /// feedback.
136    fn new(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
137        let mut returns = Vec::new();
138
139        for _ in 0..MAX {
140            match args.next_as::<P>(pa) {
141                Ok(ret) => returns.push(ret),
142                Err(err) if args.is_forming_param => return Err(err),
143                Err(_) if returns.len() >= MIN => return Ok((Self(returns), None)),
144                Err(err) => return Err(err),
145            }
146        }
147
148        if returns.len() >= MIN {
149            Ok((Self(returns), None))
150        } else {
151            Err(txt!(
152                "List needed at least [a]{MIN}[] elements, got only [a]{}",
153                returns.len()
154            ))
155        }
156    }
157}
158
159impl<const MIN: usize, const MAX: usize, P> std::ops::Deref for Between<MIN, MAX, P> {
160    type Target = Vec<P>;
161
162    fn deref(&self) -> &Self::Target {
163        &self.0
164    }
165}
166
167impl<const MIN: usize, const MAX: usize, P> std::ops::DerefMut for Between<MIN, MAX, P> {
168    fn deref_mut(&mut self) -> &mut Self::Target {
169        &mut self.0
170    }
171}
172
173impl Parameter for String {
174    fn new(_: &Pass, args: &mut Args) -> Result<(String, Option<FormId>), Text> {
175        Ok((args.next()?.to_string(), None))
176    }
177}
178
179/// Command [`Parameter`]: The remaining arguments, divided by a space
180///
181/// Fails if the [`String`] would be empty.
182pub struct Remainder(pub String);
183
184impl Parameter for Remainder {
185    fn new(_: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
186        let remainder: String = std::iter::from_fn(|| args.next().ok())
187            .collect::<Vec<&str>>()
188            .join(" ");
189        if remainder.is_empty() {
190            Err(txt!("There are no more arguments"))
191        } else {
192            Ok((Self(remainder), None))
193        }
194    }
195}
196implDeref!(Remainder, String);
197
198/// Command [`Parameter`]: An existing [`ColorScheme`]'s name
199///
200/// [`ColorScheme`]: crate::form::ColorScheme
201pub struct ColorSchemeArg(pub String);
202
203impl Parameter for ColorSchemeArg {
204    fn new(_: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
205        let scheme = args.next()?;
206        if crate::form::colorscheme_exists(scheme) {
207            Ok((ColorSchemeArg(scheme.to_string()), None))
208        } else {
209            Err(txt!("The colorscheme [a]{scheme}[] was not found"))
210        }
211    }
212}
213implDeref!(ColorSchemeArg, String);
214
215impl Parameter for Handle {
216    fn new(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
217        let buffer_name = args.next()?;
218        if let Some(handle) = crate::context::windows()
219            .buffers(pa)
220            .find(|handle| handle.read(pa).name() == buffer_name)
221        {
222            Ok((handle, Some(form::id_of!("param.buffer.open"))))
223        } else {
224            Err(txt!("No buffer called [a]{buffer_name}[] open"))
225        }
226    }
227}
228
229/// Command [`Parameter`]: An open [`Buffer`]'s name, except the
230/// current
231///
232/// [`Buffer`]: crate::buffer::Buffer
233pub struct OtherBuffer(pub Handle);
234
235impl Parameter for OtherBuffer {
236    fn new(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
237        let handle = args.next_as::<Handle>(pa)?;
238        let cur_handle = crate::context::current_buffer(pa);
239        if *cur_handle == handle {
240            Err(txt!("Argument can't be the current buffer"))
241        } else {
242            Ok((Self(handle), Some(form::id_of!("param.buffer.open"))))
243        }
244    }
245}
246implDeref!(OtherBuffer, Handle);
247
248/// Command [`Parameter`]: A file that _could_ exist
249///
250/// This is the case if the file's path has a parent that exists,
251/// or if the file itself exists.
252///
253/// [`Buffer`]: crate::buffer::Buffer
254pub struct ValidFilePath(pub PathBuf);
255
256impl Parameter for ValidFilePath {
257    fn new(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
258        let path = args.next_as::<PathBuf>(pa)?;
259
260        let canon_path = path.canonicalize();
261        let path = if let Ok(path) = &canon_path {
262            if !path.is_file() {
263                return Err(txt!("Path is not a buffer"));
264            }
265            path.clone()
266        } else if canon_path.is_err()
267            && let Ok(canon_path) = path.with_file_name(".").canonicalize()
268        {
269            canon_path.join(
270                path.file_name()
271                    .ok_or_else(|| txt!("Path has no buffer name"))?,
272            )
273        } else {
274            return Err(txt!("Path was not found"));
275        };
276
277        if let Some(parent) = path.parent()
278            && let Ok(false) | Err(_) = parent.try_exists()
279        {
280            return Err(txt!("Path's parent doesn't exist"));
281        }
282
283        let form = if crate::context::windows()
284            .buffers(pa)
285            .map(|handle| handle.read(pa).path())
286            .any(|p| std::path::Path::new(&p) == path)
287        {
288            form::id_of!("param.buffer.open")
289        } else if let Ok(true) = path.try_exists() {
290            form::id_of!("param.buffer.exists")
291        } else {
292            form::id_of!("param.buffer")
293        };
294
295        Ok((Self(path), Some(form)))
296    }
297}
298implDeref!(ValidFilePath, PathBuf);
299
300/// Comand [`Parameter`]: A [`ValidFile`], [`Handle`], `--opts` or
301/// `--opts-manifest`
302///
303/// This is a generalized way of switching to a [`Handle<Buffer`] on
304/// the `edit` and `open` commands.
305pub(super) enum PathOrBufferOrCfg {
306    Path(PathBuf),
307    Buffer(Handle),
308    Cfg,
309    CfgManifest,
310}
311
312impl Parameter for PathOrBufferOrCfg {
313    fn new(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
314        if args.flags.word("cfg") {
315            Ok((Self::Cfg, None))
316        } else if args.flags.word("cfg-manifest") {
317            Ok((Self::CfgManifest, None))
318        } else if let Ok((handle, form)) = args.next_as_with_form::<Handle>(pa) {
319            Ok((Self::Buffer(handle), form))
320        } else {
321            let (path, form) = args.next_as_with_form::<ValidFilePath>(pa)?;
322            Ok((Self::Path(path.0), form))
323        }
324    }
325}
326
327/// Command [`Parameter`]: An [`f32`] from a [`u8`] or a percentage
328///
329/// The percentage is of whole divisions of 100, 100 being equivalent
330/// to 255 in [`u8`].
331pub struct F32PercentOfU8(pub f32);
332
333impl Parameter for F32PercentOfU8 {
334    fn new(_: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
335        let arg = args.next()?;
336        if let Some(percentage) = arg.strip_suffix("%") {
337            let percentage: u8 = percentage
338                .parse()
339                .map_err(|_| txt!("[a]{arg}[] is not a valid percentage"))?;
340            if percentage <= 100 {
341                Ok((Self(percentage as f32 / 100.0), None))
342            } else {
343                Err(txt!("[a]{arg}[] is more than [a]100%"))
344            }
345        } else {
346            let byte: u8 = arg
347                .parse()
348                .map_err(|_| txt!("[a]{arg}[] couldn't be parsed"))?;
349            Ok((Self(byte as f32 / 255.0), None))
350        }
351    }
352}
353implDeref!(F32PercentOfU8, f32);
354
355impl Parameter for Color {
356    fn new(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
357        const fn hue_to_rgb(p: f32, q: f32, mut t: f32) -> f32 {
358            t = if t < 0.0 { t + 1.0 } else { t };
359            t = if t > 1.0 { t - 1.0 } else { t };
360            if t < 1.0 / 6.0 {
361                p + (q - p) * 6.0 * t
362            } else if t < 1.0 / 2.0 {
363                q
364            } else if t < 2.0 / 3.0 {
365                p + (q - p) * (2.0 / 3.0 - t) * 6.0
366            } else {
367                p
368            }
369        }
370
371        let arg = args.next()?;
372        // Expects "#{red:x}{green:x}{blue:x}"
373        if let Some(hex) = arg.strip_prefix("#") {
374            let total = match u32::from_str_radix(hex, 16) {
375                Ok(total) if hex.len() == 6 => total,
376                _ => return Err(txt!("Hexcode does not contain 6 hex values")),
377            };
378            let r = (total >> 16) as u8;
379            let g = (total >> 8) as u8;
380            let b = total as u8;
381            Ok((Color::Rgb { r, g, b }, None))
382            // Expects "rgb {red} {green} {blue}"
383        } else if arg == "rgb" {
384            let r = args.next_as::<u8>(pa)?;
385            let g = args.next_as::<u8>(pa)?;
386            let b = args.next_as::<u8>(pa)?;
387            Ok((Color::Rgb { r, g, b }, None))
388            // Expects "hsl {hue%?} {saturation%?} {lightness%?}"
389        } else if arg == "hsl" {
390            let hue = args.next_as::<F32PercentOfU8>(pa)?.0;
391            let sat = args.next_as::<F32PercentOfU8>(pa)?.0;
392            let lit = args.next_as::<F32PercentOfU8>(pa)?.0;
393            let [r, g, b] = if sat == 0.0 {
394                [lit.round() as u8; 3]
395            } else {
396                let q = if lit < 0.5 {
397                    lit * (1.0 + sat)
398                } else {
399                    lit + sat - lit * sat
400                };
401                let p = 2.0 * lit - q;
402                let r = hue_to_rgb(p, q, hue + 1.0 / 3.0);
403                let g = hue_to_rgb(p, q, hue);
404                let b = hue_to_rgb(p, q, hue - 1.0 / 3.0);
405                [r.round() as u8, g.round() as u8, b.round() as u8]
406            };
407            Ok((Color::Rgb { r, g, b }, None))
408        } else {
409            Err(txt!("Color format was not recognized"))
410        }
411    }
412}
413
414/// Command [`Parameter`]: The name of a [`Form`] that has been [set]
415///
416/// [set]: crate::form::set
417/// [`Form`]: crate::form::Form
418pub struct FormName(pub String);
419
420impl Parameter for FormName {
421    fn new(_: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
422        let arg = args.next()?;
423        if !arg.chars().all(|c| c.is_ascii_alphanumeric() || c == '.') {
424            return Err(txt!(
425                "Expected identifiers separated by '.'s, found [a]{arg}"
426            ));
427        }
428        if crate::form::exists(arg) {
429            Ok((Self(arg.to_string()), Some(form::id_of_non_static(arg))))
430        } else {
431            Err(txt!("The form [a]{arg}[] has not been set"))
432        }
433    }
434}
435implDeref!(FormName, String);
436
437impl Parameter for Flags {
438    fn new(_: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
439        Ok((args.flags.clone(), None))
440    }
441}
442
443/// The list of arguments passed to a command
444///
445/// This list excludes [`Flags`], and separates arguments either by
446/// whitespace, or by non escaped double quotes.
447///
448/// ```rust
449/// # use duat_core::cmd;
450/// # fn test() {
451/// //                    cmd │      flags      │         arguments
452/// //                   ┌   ┐│┌               ┐│┌  ┐ ┌        ┐ ┌   ┐ ┌   ┐
453/// cmd::queue_notify(r#"mycmd --flags -moreflag arg1 "more arg" \"arg arg\""#);
454/// # }
455/// ```
456pub struct Args<'a> {
457    args: Peekable<ArgsIter<'a>>,
458    param_range: Range<usize>,
459    has_to_start_param: bool,
460    is_forming_param: bool,
461    flags: Flags,
462}
463
464impl<'a> Args<'a> {
465    /// Returns the next word or quoted argument
466    #[allow(clippy::should_implement_trait)]
467    pub fn next(&mut self) -> Result<&'a str, Text> {
468        match self.args.next() {
469            Some((arg, range)) => {
470                self.param_range = range.clone();
471                if self.has_to_start_param {
472                    self.has_to_start_param = false;
473                    self.is_forming_param = true;
474                }
475                Ok(arg)
476            }
477            None => Err(txt!("Wrong argument count")),
478        }
479    }
480
481    /// Tries to parse the next argument as `P`
482    ///
483    /// If parsing fails, [`Args`] will be reset as if this function
484    /// wasn't called.
485    pub fn next_as<P: Parameter>(&mut self, pa: &Pass) -> Result<P, Text> {
486        let initial_args = self.args.clone();
487        self.has_to_start_param = true;
488        let ret = P::new(pa, self);
489        if ret.is_ok() {
490            self.is_forming_param = false;
491        } else {
492            self.args = initial_args
493        }
494        ret.map(|(arg, _)| arg)
495    }
496
497    /// Tries to parse the next argument as `P`
498    ///
499    /// If parsing fails, [`Args`] will be reset as if this function
500    /// wasn't called.
501    pub fn next_as_with_form<P: Parameter>(
502        &mut self,
503        pa: &Pass,
504    ) -> Result<(P, Option<FormId>), Text> {
505        let initial_args = self.args.clone();
506        self.has_to_start_param = true;
507        let ret = P::new(pa, self);
508        if ret.is_ok() {
509            self.is_forming_param = false;
510        } else {
511            self.args = initial_args
512        }
513        ret
514    }
515
516    /// Tries to get the next argument, otherwise returns a [`Text`]
517    pub fn next_else<T: Into<Text>>(&mut self, to_text: T) -> Result<&'a str, Text> {
518        match self.args.next() {
519            Some((arg, _)) => Ok(arg),
520            None => Err(to_text.into()),
521        }
522    }
523
524    /// Returns the char position of the next argument
525    ///
526    /// Mostly used for error feedback by the [`PromptLine`]
527    ///
528    /// [`PromptLine`]: docs.rs/duat/latest/duat/widgets/struct.PromptLine.html
529    pub fn next_start(&mut self) -> Option<usize> {
530        self.args.peek().map(|(_, r)| r.start)
531    }
532
533    /// The range of the previous [`Parameter`]
534    ///
535    /// Mostly used for error feedback by the [`PromptLine`]
536    ///
537    /// [`PromptLine`]: docs.rs/duat/latest/duat/widgets/struct.PromptLine.html
538    pub fn param_range(&self) -> Range<usize> {
539        self.param_range.clone()
540    }
541
542    /// A private [`Clone`]
543    pub(super) fn clone(&self) -> Self {
544        Self {
545            args: self.args.clone(),
546            param_range: self.param_range.clone(),
547            has_to_start_param: self.has_to_start_param,
548            is_forming_param: self.is_forming_param,
549            flags: self.flags.clone(),
550        }
551    }
552}
553
554/// The flags passed to a command
555///
556/// They work just like flags on regular Linux commands, i.e., you
557/// have word flags, like `"--global"`, and glob flags, like `"-aBc"`.
558#[derive(Clone)]
559pub struct Flags {
560    blob: String,
561    word: Vec<String>,
562}
563
564impl Flags {
565    /// Checks if all of the [`char`]s in the `blob` passed.
566    pub fn blob(&self, blob: impl AsRef<str>) -> bool {
567        let mut all_chars = true;
568        for char in blob.as_ref().chars() {
569            all_chars &= self.blob.contains(char);
570        }
571        all_chars
572    }
573
574    /// Returns `true` if the `word` flag was passed.
575    pub fn word(&self, flag: impl AsRef<str>) -> bool {
576        self.word.iter().any(|w| w == flag.as_ref())
577    }
578
579    /// Returns `true` if no flags have been passed.
580    pub fn is_empty(&self) -> bool {
581        self.blob.is_empty() && self.word.is_empty()
582    }
583}
584
585/// Splits a command into [`Args`] and [`Flags`]
586///
587/// [`Args`]: super::Args
588/// [`Flags`]: super::Flags
589pub fn get_args(command: &str) -> super::Args<'_> {
590    let mut blob = String::new();
591    let mut word = Vec::new();
592
593    let args = ArgsIter::new(command);
594    let mut args = args.peekable();
595    let mut byte = 0;
596
597    while let Some((arg, range)) = args.peek() {
598        if let Some(word_arg) = arg.strip_prefix("--") {
599            if !word_arg.is_empty() {
600                args.next();
601                if !word.iter().any(|w| w == word_arg) {
602                    word.push(word_arg.to_string())
603                }
604            } else {
605                args.next();
606                break;
607            }
608        } else if let Some(blob_arg) = arg.strip_prefix('-') {
609            args.next();
610            for char in blob_arg.chars() {
611                if !blob.contains(char) {
612                    blob.push(char)
613                }
614            }
615        } else {
616            byte = range.start;
617            break;
618        }
619    }
620
621    super::Args {
622        args,
623        param_range: byte..byte,
624        has_to_start_param: false,
625        is_forming_param: false,
626        flags: super::Flags { blob, word },
627    }
628}
629
630/// A iterator over arguments in a `&str`, useful for the [`cmd`]
631/// module
632///
633/// [`cmd`]: super
634#[derive(Clone)]
635pub struct ArgsIter<'a> {
636    command: &'a str,
637    chars: std::str::CharIndices<'a>,
638    start: Option<usize>,
639    end: Option<usize>,
640    is_quoting: bool,
641    last_char: char,
642}
643
644impl<'a> ArgsIter<'a> {
645    /// Returns a new iterator over arguments in a `&str`
646    pub fn new(command: &'a str) -> Self {
647        let mut args_iter = Self {
648            command,
649            chars: command.char_indices(),
650            start: None,
651            end: None,
652            is_quoting: false,
653            // Initial value doesn't matter, as long as it's not '\'
654            last_char: 'a',
655        };
656
657        args_iter.next();
658        args_iter
659    }
660}
661
662impl<'a> Iterator for ArgsIter<'a> {
663    type Item = (&'a str, Range<usize>);
664
665    fn next(&mut self) -> Option<Self::Item> {
666        while let Some((b, char)) = self.chars.next() {
667            let lc = self.last_char;
668            self.last_char = char;
669            if self.start.is_some() && char.is_whitespace() && !self.is_quoting {
670                self.end = Some(b);
671                break;
672            } else if char == '"' && lc != '\\' {
673                self.is_quoting = !self.is_quoting;
674                if !self.is_quoting {
675                    self.end = Some(b + 1);
676                    break;
677                } else {
678                    self.start = Some(b);
679                }
680            } else if !char.is_whitespace() && self.start.is_none() {
681                self.start = Some(b);
682            }
683        }
684
685        let e = self.end.take().unwrap_or(self.command.len());
686        self.start.take().map(|s| (&self.command[s..e], s..e))
687    }
688}
689
690macro_rules! parse_impl {
691    ($t:ty) => {
692        impl Parameter for $t {
693            fn new(_: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
694                let arg = args.next()?;
695                let arg = arg
696                    .parse()
697                    .map_err(|_| txt!("[a]{arg}[] couldn't be parsed as [a]{}[]", stringify!($t)));
698                arg.map(|arg| (arg, None))
699            }
700        }
701    };
702}
703
704parse_impl!(bool);
705parse_impl!(u8);
706parse_impl!(u16);
707parse_impl!(u32);
708parse_impl!(u64);
709parse_impl!(u128);
710parse_impl!(usize);
711parse_impl!(i8);
712parse_impl!(i16);
713parse_impl!(i32);
714parse_impl!(i64);
715parse_impl!(i128);
716parse_impl!(isize);
717parse_impl!(f32);
718parse_impl!(f64);
719parse_impl!(std::path::PathBuf);