argot_cmd/model/mod.rs
1//! Data model for argot commands.
2//!
3//! Every item in the argot command tree is represented by a [`Command`]. Related
4//! types — [`Argument`], [`Flag`], [`Example`] — attach metadata that drives
5//! both parsing and documentation generation.
6//!
7//! ## Builder Pattern
8//!
9//! All model types are constructed through consuming builders:
10//!
11//! ```
12//! # use argot_cmd::model::{Command, Argument, Flag, Example};
13//! let cmd = Command::builder("deploy")
14//! .summary("Deploy the application")
15//! .argument(
16//! Argument::builder("env")
17//! .description("Target environment")
18//! .required()
19//! .build()
20//! .unwrap(),
21//! )
22//! .flag(
23//! Flag::builder("dry-run")
24//! .short('n')
25//! .description("Simulate without making changes")
26//! .build()
27//! .unwrap(),
28//! )
29//! .build()
30//! .unwrap();
31//!
32//! assert_eq!(cmd.canonical, "deploy");
33//! ```
34//!
35//! ## Handler Functions and Parsed Commands
36//!
37//! A [`HandlerFn`] is an `Arc`-wrapped closure that receives a [`ParsedCommand`]
38//! reference and returns `Result<(), Box<dyn Error>>`. The `Arc` wrapper means
39//! cloning a [`Command`] only bumps a reference count — no deep copy of the
40//! closure occurs.
41//!
42//! [`ParsedCommand`] is the output of a successful parse: it borrows the matched
43//! [`Command`] from the registry and owns the resolved argument and flag maps.
44
45/// Positional argument definition and builder.
46pub mod argument;
47/// Command definition, builder, handler type, and parsed command output.
48pub mod command;
49/// Usage example type for commands.
50pub mod example;
51/// Named flag definition and builder.
52pub mod flag;
53
54pub use argument::{Argument, ArgumentBuilder};
55#[cfg(feature = "async")]
56pub use command::AsyncHandlerFn;
57pub use command::{Command, CommandBuilder, HandlerFn, ParsedCommand};
58pub use example::Example;
59pub use flag::{Flag, FlagBuilder};
60
61use thiserror::Error;
62
63/// Error returned by builder `build()` methods.
64///
65/// Variants are returned from [`CommandBuilder::build`], [`ArgumentBuilder::build`],
66/// and [`FlagBuilder::build`] when validation fails. The list of variants includes
67/// checks for empty names, duplicate aliases, duplicate flags, duplicate arguments,
68/// duplicate subcommands, and variadic argument ordering.
69///
70/// # Examples
71///
72/// ```
73/// # use argot_cmd::model::{Command, BuildError};
74/// assert_eq!(Command::builder("").build().unwrap_err(), BuildError::EmptyCanonical);
75/// ```
76#[derive(Debug, Error, PartialEq)]
77pub enum BuildError {
78 /// The canonical name (or argument/flag name) was empty or whitespace.
79 #[error("canonical name must not be empty")]
80 EmptyCanonical,
81
82 /// Two aliases on the same command share the same string.
83 #[error("duplicate alias `{0}`")]
84 DuplicateAlias(String),
85
86 /// An alias is identical to the command's canonical name.
87 #[error("alias `{0}` duplicates the canonical name")]
88 AliasEqualsCanonical(String),
89
90 /// Two flags on the same command share the same long name.
91 #[error("duplicate flag name `{0}`")]
92 DuplicateFlagName(String),
93
94 /// Two flags on the same command share the same short character.
95 #[error("duplicate short flag `-{0}`")]
96 DuplicateShortFlag(char),
97
98 /// Two positional arguments on the same command share the same name.
99 #[error("duplicate argument name `{0}`")]
100 DuplicateArgumentName(String),
101
102 /// Two subcommands at the same level share the same canonical name.
103 #[error("duplicate subcommand `{0}`")]
104 DuplicateSubcommandName(String),
105
106 /// A variadic argument is not the last argument defined.
107 #[error("variadic argument `{0}` must be the last argument")]
108 VariadicNotLast(String),
109
110 /// A flag's `choices` list is empty, which would reject all values.
111 #[error("flag `{0}` has an empty choices list")]
112 EmptyChoices(String),
113
114 /// A mutual-exclusivity group contains fewer than two flag names.
115 #[error("exclusive group must contain at least two flags")]
116 ExclusiveGroupTooSmall,
117
118 /// A flag referenced in a mutual-exclusivity group is not defined on the command.
119 #[error("flag `{0}` in exclusive group is not defined on this command")]
120 ExclusiveGroupUnknownFlag(String),
121}