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 anywhere: bool,
10 help: Option<Doc>,
11 },
12 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 Flag {
26 name: ShortLong,
27 shorts: Vec<char>,
29 env: Option<&'static str>,
30 help: Option<Doc>,
31 },
32 Argument {
36 name: ShortLong,
37 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 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 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}