1use std::{fmt::Display, hash::Hash};
4
5#[inline]
7pub fn short<S: Into<char>>(s: S) -> Full {
8 Full::from(Cli::Short(s.into()))
9}
10
11#[inline]
13#[allow(clippy::needless_pass_by_value)]
14pub fn long<L: Into<String>>(l: L) -> Full {
15 Full::from(Cli::Long(l.into()))
16}
17
18#[inline]
20#[allow(clippy::needless_pass_by_value)]
21pub fn both<S: Into<char>, L: Into<String>>(s: S, l: L) -> Full {
22 Full::from(Cli::Both(s.into(), l.into()))
23}
24
25#[inline]
27#[allow(clippy::needless_pass_by_value)]
28pub fn env<E: Into<String>>(e: E) -> Full {
29 Full {
30 cli: None,
31 env: Some(e.into()),
32
33 #[cfg(feature = "help")]
34 doc: None,
35 }
36}
37
38#[derive(Debug, Clone)]
43pub struct Full {
44 pub(crate) cli: Option<Cli>,
45 pub(crate) env: Option<String>,
46
47 #[cfg(feature = "help")]
49 pub doc: Option<String>,
50}
51
52impl Full {
53 #[must_use]
55 pub fn cli(mut self, tag: Cli) -> Self {
56 self.cli = Some(tag);
57 self
58 }
59
60 #[must_use]
62 #[allow(clippy::needless_pass_by_value)]
63 pub fn env<S: Into<String>>(mut self, name: S) -> Self {
64 self.env = Some(name.into());
65 self
66 }
67
68 #[must_use]
73 #[cfg(feature = "help")]
74 pub fn doc<S: Into<String>>(mut self, doc: S) -> Self {
75 let doc = doc.into();
76 if doc.is_empty() {
77 self.doc = None;
78 self
79 } else {
80 self.doc = Some(doc);
81 self
82 }
83 }
84
85 pub fn has_cli(&self) -> bool {
87 self.cli.is_some()
88 }
89
90 pub fn has_env(&self) -> bool {
92 self.env.is_some()
93 }
94
95 pub fn matches_cli(&self, tag: &str) -> bool {
98 self.cli.as_ref().map_or(false, |t| t.matches(tag))
99 }
100
101 pub fn matches_long(&self, long: &str) -> bool {
104 self.cli
105 .as_ref()
106 .map_or(false, |tag| tag.matches_long(long))
107 }
108
109 pub fn matches_short(&self, short: char) -> bool {
112 self.cli
113 .as_ref()
114 .map_or(false, |tag| tag.matches_short(short))
115 }
116
117 pub fn matches_env(&self, env: &str) -> bool {
120 self.env.as_ref().map_or(false, |arg| arg == env)
121 }
122}
123
124impl From<Cli> for Full {
125 fn from(tag: Cli) -> Self {
126 Self {
127 cli: Some(tag),
128 env: None,
129
130 #[cfg(feature = "help")]
131 doc: None,
132 }
133 }
134}
135
136impl Hash for Full {
137 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
138 if let Some(tag) = &self.cli {
139 core::mem::discriminant(tag).hash(state);
140 }
141
142 if let Some(arg) = &self.env {
143 arg.hash(state);
144 }
145 }
146}
147
148#[derive(Debug, Clone)]
156pub enum Cli {
157 Short(char),
159 Long(String),
161 Both(char, String),
163}
164
165impl Cli {
166 pub fn env(self, env: String) -> Full {
168 Full {
169 cli: Some(self),
170 env: Some(env),
171
172 #[cfg(feature = "help")]
173 doc: None,
174 }
175 }
176
177 pub fn matches(&self, tag: &str) -> bool {
180 if let Some(tag) = tag.strip_prefix("--") {
181 self.matches_long(tag)
182 } else if let Some(tag) = tag.strip_prefix('-') {
183 if let Some(ch) = tag.chars().next() {
184 self.matches_short(ch)
185 } else {
186 false
187 }
188 } else {
189 false
190 }
191 }
192
193 pub fn matches_long(&self, long: &str) -> bool {
196 match self {
197 Cli::Short(_) => false,
198 Cli::Long(l) | Cli::Both(_, l) => l == long,
199 }
200 }
201
202 pub fn matches_short(&self, short: char) -> bool {
205 match self {
206 Cli::Long(_) => false,
207 Cli::Short(s) | Cli::Both(s, _) => *s == short,
208 }
209 }
210}
211
212impl PartialEq for Cli {
213 fn eq(&self, other: &Self) -> bool {
214 match self {
215 Self::Short(s) => match other {
216 Self::Short(o) | Self::Both(o, _) => s == o,
217 Self::Long(_) => false,
218 },
219 Self::Long(s) => match other {
220 Self::Long(o) | Self::Both(_, o) => s == o,
221 Self::Short(_) => false,
222 },
223 Self::Both(s1, s2) => match other {
224 Self::Short(o) => s1 == o,
225 Self::Long(o) => s2 == o,
226 Self::Both(o1, o2) => (s1 == o1) || (s2 == o2),
227 },
228 }
229 }
230}
231
232impl Display for Cli {
233 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
234 match self {
235 Self::Short(ch) => write!(f, "-{ch}"),
236 Self::Long(s) => write!(f, "--{s}"),
237 Self::Both(ch, s) => write!(f, "-{ch} / --{s}"),
238 }
239 }
240}