bpaf/
item.rs

1use crate::{info::Info, meta_help::Metavar, parsers::NamedArg, Doc, Meta};
2
3#[doc(hidden)]
4#[derive(Clone, Debug)]
5pub enum Item {
6    Any {
7        metavar: Doc,
8        /// used by any, moves it from positionals into arguments
9        anywhere: bool,
10        help: Option<Doc>,
11    },
12    /// Positional item, consumed from the the front of the arguments
13    /// <FILE>
14    Positional { metavar: Metavar, help: Option<Doc> },
15    Command {
16        name: &'static str,
17        short: Option<char>,
18        help: Option<Doc>,
19        meta: Box<Meta>,
20        info: Box<Info>,
21    },
22    /// short or long name, consumed anywhere
23    /// -f
24    /// --file
25    Flag {
26        name: ShortLong,
27        /// used for disambiguation
28        shorts: Vec<char>,
29        env: Option<&'static str>,
30        help: Option<Doc>,
31    },
32    /// Short or long name followed by a value, consumed anywhere
33    /// -f <VAL>
34    /// --file <VAL>
35    Argument {
36        name: ShortLong,
37        /// used for disambiguation
38        shorts: Vec<char>,
39        metavar: Metavar,
40        env: Option<&'static str>,
41        help: Option<Doc>,
42    },
43}
44
45impl Item {
46    pub(crate) fn is_pos(&self) -> bool {
47        match self {
48            Item::Any { anywhere, .. } => !anywhere,
49            Item::Positional { .. } | Item::Command { .. } => true,
50            Item::Flag { .. } | Item::Argument { .. } => false,
51        }
52    }
53    /// Normalize name inside [`ShortLong`] into either short or long
54    pub(crate) fn normalize(&mut self, short: bool) {
55        match self {
56            Item::Positional { .. } | Item::Command { .. } | Item::Any { .. } => {}
57            Item::Flag { name, .. } | Item::Argument { name, .. } => name.normalize(short),
58        }
59    }
60}
61
62#[doc(hidden)]
63#[derive(Copy, Clone, Debug)]
64pub enum ShortLong {
65    Short(char),
66    Long(&'static str),
67    Both(char, &'static str),
68}
69
70impl ShortLong {
71    pub(crate) fn as_long(&self) -> Option<&'static str> {
72        match self {
73            ShortLong::Long(l) | ShortLong::Both(_, l) => Some(l),
74            ShortLong::Short(_) => None,
75        }
76    }
77    pub(crate) fn as_short(&self) -> Option<char> {
78        match self {
79            ShortLong::Short(s) | ShortLong::Both(s, _) => Some(*s),
80            ShortLong::Long(_) => None,
81        }
82    }
83}
84
85impl PartialEq<&str> for ShortLong {
86    fn eq(&self, other: &&str) -> bool {
87        fn short_eq(c: char, s: &str) -> bool {
88            let mut tmp = [0u8; 4];
89            s.strip_prefix('-') == Some(c.encode_utf8(&mut tmp))
90        }
91        fn long_eq(l: &str, s: &str) -> bool {
92            Some(l) == s.strip_prefix("--")
93        }
94        match self {
95            ShortLong::Short(s) => short_eq(*s, other),
96            ShortLong::Long(l) => long_eq(l, other),
97            ShortLong::Both(s, l) => short_eq(*s, other) || long_eq(l, other),
98        }
99    }
100}
101
102impl ShortLong {
103    /// Changes [`ShortLong`](ShortLong::ShortLong) variant into either short or long depending,
104    /// leaves both Short and Long untouched
105    pub(crate) fn normalize(&mut self, short: bool) {
106        match self {
107            ShortLong::Short(_) | ShortLong::Long(_) => {}
108            ShortLong::Both(s, l) => {
109                if short {
110                    *self = Self::Short(*s);
111                } else {
112                    *self = Self::Long(l);
113                }
114            }
115        }
116    }
117}
118
119impl TryFrom<&NamedArg> for ShortLong {
120    type Error = ();
121
122    fn try_from(named: &NamedArg) -> Result<Self, Self::Error> {
123        match (named.short.is_empty(), named.long.is_empty()) {
124            (true, true) => Err(()),
125            (true, false) => Ok(Self::Long(named.long[0])),
126            (false, true) => Ok(Self::Short(named.short[0])),
127            (false, false) => Ok(Self::Both(named.short[0], named.long[0])),
128        }
129    }
130}
131
132impl Item {
133    #[must_use]
134    pub(crate) fn required(self, required: bool) -> Meta {
135        let boxed = Meta::from(self);
136        if required {
137            boxed
138        } else {
139            Meta::Optional(Box::new(boxed))
140        }
141    }
142}