1use std::fmt;
14
15#[derive(Clone, Debug, PartialEq, Eq)]
16pub struct CliSpec {
17 name: String,
18 about: String,
19 version: Option<String>,
20 commands: Vec<CommandSpec>,
21 options: Vec<OptionSpec>,
22 arguments: Vec<ArgumentSpec>,
23 command_required: bool,
24}
25
26#[derive(Clone, Debug, PartialEq, Eq)]
27pub struct CommandSpec {
28 name: String,
29 aliases: Vec<String>,
30 about: String,
31 commands: Vec<CommandSpec>,
32 options: Vec<OptionSpec>,
33 arguments: Vec<ArgumentSpec>,
34 command_required: bool,
35 hidden: bool,
36}
37
38#[derive(Clone, Debug, PartialEq, Eq)]
39pub struct OptionSpec {
40 long: String,
41 short: Option<char>,
42 value: ValueMode,
43 value_name: String,
44 value_type: ValueType,
45 possible_values: Vec<String>,
46 about: String,
47 required: bool,
48 repeatable: bool,
49 detached_optional_value: bool,
50 terminal: bool,
51 hidden: bool,
52 conflicts_with: Vec<String>,
53 requires: Vec<String>,
54 pub(crate) environment: Option<String>,
55 pub(crate) default: Option<std::ffi::OsString>,
56 pub(crate) kind: crate::ValueKind,
57 pub(crate) exclusive_group: Option<String>,
58}
59
60#[derive(Clone, Debug, PartialEq, Eq)]
61pub struct ArgumentSpec {
62 name: String,
63 about: String,
64 value_type: ValueType,
65 possible_values: Vec<String>,
66 required: bool,
67 multiple: bool,
68 pub(crate) kind: crate::ValueKind,
69}
70
71#[derive(Clone, Copy, Debug, PartialEq, Eq)]
72pub enum ValueMode {
73 Forbidden,
74 Required,
75 Optional,
76}
77
78#[derive(Clone, Copy, Debug, PartialEq, Eq)]
79pub enum ValueType {
80 String,
81 Bool,
82 I64,
83 U64,
84}
85
86impl fmt::Display for ValueType {
87 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
88 formatter.write_str(match self {
89 Self::String => "text",
90 Self::Bool => "true or false",
91 Self::I64 => "a signed integer",
92 Self::U64 => "an unsigned integer",
93 })
94 }
95}
96
97#[derive(Clone, Debug, PartialEq, Eq)]
98pub struct SpecError {
99 message: String,
100}
101
102impl CliSpec {
103 pub fn new(name: impl Into<String>) -> Self {
104 Self {
105 name: name.into(),
106 about: String::new(),
107 version: None,
108 commands: Vec::new(),
109 options: Vec::new(),
110 arguments: Vec::new(),
111 command_required: false,
112 }
113 }
114 pub fn about(mut self, about: impl Into<String>) -> Self {
115 self.about = about.into();
116 self
117 }
118 pub fn version(mut self, version: impl Into<String>) -> Self {
119 self.version = Some(version.into());
120 self
121 }
122 pub fn command(mut self, command: CommandSpec) -> Self {
123 self.commands.push(command);
124 self
125 }
126 pub fn option(mut self, option: OptionSpec) -> Self {
127 self.options.push(option);
128 self
129 }
130 pub fn argument(mut self, argument: ArgumentSpec) -> Self {
131 self.arguments.push(argument);
132 self
133 }
134 pub fn command_required(mut self, required: bool) -> Self {
135 self.command_required = required;
136 self
137 }
138 pub fn validate(&self) -> Result<(), SpecError> {
139 crate::spec_validation::validate_spec(self)
140 }
141 pub fn name(&self) -> &str {
142 &self.name
143 }
144 pub fn about_text(&self) -> &str {
145 &self.about
146 }
147 pub fn version_text(&self) -> Option<&str> {
148 self.version.as_deref()
149 }
150 pub fn commands(&self) -> &[CommandSpec] {
151 &self.commands
152 }
153 pub fn options(&self) -> &[OptionSpec] {
154 &self.options
155 }
156 pub fn arguments(&self) -> &[ArgumentSpec] {
157 &self.arguments
158 }
159 pub fn is_command_required(&self) -> bool {
160 self.command_required
161 }
162}
163
164impl CommandSpec {
165 pub fn new(name: impl Into<String>) -> Self {
166 Self {
167 name: name.into(),
168 aliases: Vec::new(),
169 about: String::new(),
170 commands: Vec::new(),
171 options: Vec::new(),
172 arguments: Vec::new(),
173 command_required: false,
174 hidden: false,
175 }
176 }
177 pub fn alias(mut self, alias: impl Into<String>) -> Self {
178 self.aliases.push(alias.into());
179 self
180 }
181 pub fn about(mut self, about: impl Into<String>) -> Self {
182 self.about = about.into();
183 self
184 }
185 pub fn command(mut self, command: CommandSpec) -> Self {
186 self.commands.push(command);
187 self
188 }
189 pub fn option(mut self, option: OptionSpec) -> Self {
190 self.options.push(option);
191 self
192 }
193 pub fn argument(mut self, argument: ArgumentSpec) -> Self {
194 self.arguments.push(argument);
195 self
196 }
197 pub fn command_required(mut self, required: bool) -> Self {
198 self.command_required = required;
199 self
200 }
201 pub fn hidden(mut self, hidden: bool) -> Self {
202 self.hidden = hidden;
203 self
204 }
205 pub fn name(&self) -> &str {
206 &self.name
207 }
208 pub fn aliases(&self) -> &[String] {
209 &self.aliases
210 }
211 pub fn matches(&self, value: &str) -> bool {
212 self.name == value || self.aliases.iter().any(|alias| alias == value)
213 }
214 pub fn about_text(&self) -> &str {
215 &self.about
216 }
217 pub fn commands(&self) -> &[CommandSpec] {
218 &self.commands
219 }
220 pub fn options(&self) -> &[OptionSpec] {
221 &self.options
222 }
223 pub fn arguments(&self) -> &[ArgumentSpec] {
224 &self.arguments
225 }
226 pub fn is_command_required(&self) -> bool {
227 self.command_required
228 }
229 pub fn is_hidden(&self) -> bool {
230 self.hidden
231 }
232}
233
234impl OptionSpec {
235 pub fn flag(long: impl Into<String>) -> Self {
236 Self::new(long, ValueMode::Forbidden)
237 }
238 pub fn value(long: impl Into<String>) -> Self {
239 Self::new(long, ValueMode::Required)
240 }
241 fn new(long: impl Into<String>, value: ValueMode) -> Self {
242 Self {
243 long: long.into(),
244 short: None,
245 value,
246 value_name: "VALUE".into(),
247 value_type: ValueType::String,
248 possible_values: Vec::new(),
249 about: String::new(),
250 required: false,
251 repeatable: false,
252 detached_optional_value: false,
253 terminal: false,
254 hidden: false,
255 conflicts_with: Vec::new(),
256 requires: Vec::new(),
257 environment: None,
258 default: None,
259 kind: crate::ValueKind::Text,
260 exclusive_group: None,
261 }
262 }
263 pub fn short(mut self, short: char) -> Self {
264 self.short = Some(short);
265 self
266 }
267 pub fn optional_value(mut self) -> Self {
268 self.value = ValueMode::Optional;
269 self
270 }
271 pub fn value_name(mut self, name: impl Into<String>) -> Self {
272 self.value_name = name.into();
273 self
274 }
275 pub fn value_type(mut self, value_type: ValueType) -> Self {
276 self.value_type = value_type;
277 self
278 }
279 pub fn possible_value(mut self, value: impl Into<String>) -> Self {
280 self.possible_values.push(value.into());
281 self
282 }
283 pub fn about(mut self, about: impl Into<String>) -> Self {
284 self.about = about.into();
285 self
286 }
287 pub fn required(mut self, required: bool) -> Self {
288 self.required = required;
289 self
290 }
291 pub fn repeatable(mut self, repeatable: bool) -> Self {
292 self.repeatable = repeatable;
293 self
294 }
295 pub fn detached_optional_value(mut self, enabled: bool) -> Self {
301 self.detached_optional_value = enabled;
302 self
303 }
304 pub fn terminal(mut self, terminal: bool) -> Self {
305 self.terminal = terminal;
306 self
307 }
308 pub fn hidden(mut self, hidden: bool) -> Self {
309 self.hidden = hidden;
310 self
311 }
312 pub fn conflicts_with(mut self, long: impl Into<String>) -> Self {
313 self.conflicts_with.push(long.into());
314 self
315 }
316 pub fn requires(mut self, long: impl Into<String>) -> Self {
317 self.requires.push(long.into());
318 self
319 }
320 pub fn env(mut self, name: impl Into<String>) -> Self {
322 self.environment = Some(name.into());
323 self
324 }
325 pub fn default_value(mut self, value: impl Into<std::ffi::OsString>) -> Self {
327 self.default = Some(value.into());
328 self
329 }
330 pub fn value_kind(mut self, kind: crate::ValueKind) -> Self {
332 self.kind = kind;
333 self
334 }
335 pub fn exclusive_group(mut self, name: impl Into<String>) -> Self {
337 self.exclusive_group = Some(name.into());
338 self
339 }
340 pub fn long(&self) -> &str {
341 &self.long
342 }
343 pub fn short_name(&self) -> Option<char> {
344 self.short
345 }
346 pub fn value_mode(&self) -> ValueMode {
347 self.value
348 }
349 pub fn value_name_text(&self) -> &str {
350 &self.value_name
351 }
352 pub fn value_type_kind(&self) -> ValueType {
353 self.value_type
354 }
355 pub fn possible_values(&self) -> &[String] {
356 &self.possible_values
357 }
358 pub fn about_text(&self) -> &str {
359 &self.about
360 }
361 pub fn is_required(&self) -> bool {
362 self.required
363 }
364 pub fn is_repeatable(&self) -> bool {
365 self.repeatable
366 }
367 pub fn accepts_detached_optional_value(&self) -> bool {
368 self.detached_optional_value
369 }
370 pub fn is_terminal(&self) -> bool {
371 self.terminal
372 }
373 pub fn is_hidden(&self) -> bool {
374 self.hidden
375 }
376 pub fn conflicts(&self) -> &[String] {
377 &self.conflicts_with
378 }
379 pub fn requirements(&self) -> &[String] {
380 &self.requires
381 }
382}
383
384impl ArgumentSpec {
385 pub fn new(name: impl Into<String>) -> Self {
386 Self {
387 name: name.into(),
388 about: String::new(),
389 value_type: ValueType::String,
390 possible_values: Vec::new(),
391 required: false,
392 multiple: false,
393 kind: crate::ValueKind::Text,
394 }
395 }
396 pub fn about(mut self, about: impl Into<String>) -> Self {
397 self.about = about.into();
398 self
399 }
400 pub fn value_type(mut self, value_type: ValueType) -> Self {
401 self.value_type = value_type;
402 self
403 }
404 pub fn possible_value(mut self, value: impl Into<String>) -> Self {
405 self.possible_values.push(value.into());
406 self
407 }
408 pub fn required(mut self, required: bool) -> Self {
409 self.required = required;
410 self
411 }
412 pub fn multiple(mut self, multiple: bool) -> Self {
413 self.multiple = multiple;
414 self
415 }
416 pub fn value_kind(mut self, kind: crate::ValueKind) -> Self {
418 self.kind = kind;
419 self
420 }
421 pub fn name(&self) -> &str {
422 &self.name
423 }
424 pub fn about_text(&self) -> &str {
425 &self.about
426 }
427 pub fn value_type_kind(&self) -> ValueType {
428 self.value_type
429 }
430 pub fn possible_values(&self) -> &[String] {
431 &self.possible_values
432 }
433 pub fn is_required(&self) -> bool {
434 self.required
435 }
436 pub fn is_multiple(&self) -> bool {
437 self.multiple
438 }
439}
440
441impl SpecError {
442 fn new(message: impl Into<String>) -> Self {
443 Self {
444 message: message.into(),
445 }
446 }
447 pub(crate) fn new_internal(message: impl Into<String>) -> Self {
448 Self::new(message)
449 }
450}
451impl fmt::Display for SpecError {
452 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
453 f.write_str(&self.message)
454 }
455}
456impl std::error::Error for SpecError {}