rust_args_parser/
spec.rs

1use crate::Result;
2use std::ffi::{OsStr, OsString};
3
4/// Color mode for help rendering.
5#[derive(Clone, Copy, Debug)]
6pub enum ColorMode {
7    Auto,
8    Always,
9    Never,
10}
11
12/// Global environment for a parse/render session.
13#[derive(Clone, Copy, Debug)]
14pub struct Env {
15    /// Wrap columns for help. `0` means no wrapping.
16    pub wrap_cols: usize,
17    /// Whether to colorize help (honors `NO_COLOR` when `color` feature is enabled).
18    pub color: ColorMode,
19    /// Whether to compute suggestions on errors (if enabled).
20    pub suggest: bool,
21    /// Built-ins
22    pub auto_help: bool,
23    pub version: Option<&'static str>,
24    pub author: Option<&'static str>,
25}
26impl Default for Env {
27    fn default() -> Self {
28        Self {
29            wrap_cols: 80,
30            color: ColorMode::Auto,
31            suggest: true,
32            auto_help: true,
33            version: None,
34            author: None,
35        }
36    }
37}
38
39/// Whether an option may be repeated.
40#[derive(Clone, Copy, Debug, PartialEq, Eq)]
41pub enum Repeat {
42    Single,
43    Many,
44}
45
46/// Group rule (applies to a set of options sharing the same group name).
47#[derive(Clone, Copy, Debug, PartialEq, Eq)]
48pub enum GroupMode {
49    Xor,
50    ReqOne,
51}
52
53/// Provenance of a value in `Matches`.
54#[derive(Clone, Copy, Debug, PartialEq, Eq)]
55pub enum Source {
56    Cli,
57    Env,
58    Default,
59}
60
61/// User-pluggable validator for a single value (OsStr-based, cross-platform).
62pub type ValueValidator = fn(&OsStr) -> Result<()>;
63
64/// Command-level validator that can inspect the final `Matches`.
65pub type CmdValidator = fn(&crate::Matches) -> Result<()>;
66
67/// Command handler (executed for the **leaf** command after callbacks).
68pub type CmdHandler<Ctx> = fn(&crate::Matches, &mut Ctx) -> Result<()>;
69
70/// Callback to apply a value/flag into user context.
71pub type OnValue<Ctx> = fn(&OsStr, &mut Ctx) -> Result<()>;
72pub type OnFlag<Ctx> = fn(&mut Ctx) -> Result<()>;
73
74/// Option (flag or value-bearing).
75pub struct OptSpec<'a, Ctx: ?Sized> {
76    name: &'a str,
77    short: Option<char>,
78    long: Option<&'a str>,
79    metavar: Option<&'a str>,
80    help: Option<&'a str>,
81    env: Option<&'a str>,
82    default: Option<OsString>,
83    group: Option<&'a str>,
84    repeat: Repeat,
85    takes_value: bool,
86    on_value: Option<OnValue<Ctx>>, // value setter
87    on_flag: Option<OnFlag<Ctx>>,   // flag setter
88    validator: Option<ValueValidator>,
89}
90
91impl<'a, Ctx: ?Sized> OptSpec<'a, Ctx> {
92    /// Create a **flag** option. Other fields are set via builder methods.
93    pub fn flag(name: &'a str, cb: OnFlag<Ctx>) -> Self {
94        Self {
95            name,
96            short: None,
97            long: None,
98            metavar: None,
99            help: None,
100            env: None,
101            default: None,
102            group: None,
103            repeat: Repeat::Single,
104            takes_value: false,
105            on_value: None,
106            on_flag: Some(cb),
107            validator: None,
108        }
109    }
110    /// Create a **value** option. Other fields are set via builder methods.
111    pub const fn value(name: &'a str, cb: OnValue<Ctx>) -> Self {
112        Self {
113            name,
114            short: None,
115            long: None,
116            metavar: None,
117            help: None,
118            env: None,
119            default: None,
120            group: None,
121            repeat: Repeat::Single,
122            takes_value: true,
123            on_value: Some(cb),
124            on_flag: None,
125            validator: None,
126        }
127    }
128    // --- builders ---
129    #[must_use]
130    pub const fn short(mut self, s: char) -> Self {
131        self.short = Some(s);
132        self
133    }
134    #[must_use]
135    pub const fn long(mut self, l: &'a str) -> Self {
136        self.long = Some(l);
137        self
138    }
139    #[must_use]
140    pub const fn metavar(mut self, mv: &'a str) -> Self {
141        self.metavar = Some(mv);
142        self
143    }
144    #[must_use]
145    pub const fn help(mut self, h: &'a str) -> Self {
146        self.help = Some(h);
147        self
148    }
149    #[must_use]
150    pub const fn env(mut self, name: &'a str) -> Self {
151        self.env = Some(name);
152        self
153    }
154    #[must_use]
155    pub fn default(mut self, val: impl Into<OsString>) -> Self {
156        self.default = Some(val.into());
157        self
158    }
159    #[must_use]
160    pub const fn group(mut self, g: &'a str) -> Self {
161        self.group = Some(g);
162        self
163    }
164    #[must_use]
165    pub const fn single(mut self) -> Self {
166        self.repeat = Repeat::Single;
167        self
168    }
169    #[must_use]
170    pub const fn repeatable(mut self) -> Self {
171        self.repeat = Repeat::Many;
172        self
173    }
174    #[must_use]
175    pub const fn validator(mut self, v: ValueValidator) -> Self {
176        self.validator = Some(v);
177        self
178    }
179
180    // --- getters (get_*; booleans use is_*) ---
181    #[must_use]
182    pub const fn get_name(&self) -> &str {
183        self.name
184    }
185    #[must_use]
186    pub const fn get_short(&self) -> Option<char> {
187        self.short
188    }
189    #[must_use]
190    pub const fn get_long(&self) -> Option<&str> {
191        self.long
192    }
193    #[must_use]
194    pub const fn get_metavar(&self) -> Option<&str> {
195        self.metavar
196    }
197    #[must_use]
198    pub const fn get_help(&self) -> Option<&str> {
199        self.help
200    }
201    #[must_use]
202    pub const fn get_env(&self) -> Option<&str> {
203        self.env
204    }
205    #[must_use]
206    pub const fn get_default(&self) -> Option<&OsString> {
207        self.default.as_ref()
208    }
209    #[must_use]
210    pub const fn get_group(&self) -> Option<&str> {
211        self.group
212    }
213    #[must_use]
214    pub const fn is_value(&self) -> bool {
215        self.takes_value
216    }
217    #[must_use]
218    pub const fn get_repeat(&self) -> Repeat {
219        self.repeat
220    }
221    #[must_use]
222    pub fn get_on_value(&self) -> Option<OnValue<Ctx>> {
223        self.on_value
224    }
225    #[must_use]
226    pub fn get_on_flag(&self) -> Option<OnFlag<Ctx>> {
227        self.on_flag
228    }
229    #[must_use]
230    pub fn get_validator(&self) -> Option<ValueValidator> {
231        self.validator
232    }
233}
234
235/// Positional cardinality.
236#[derive(Clone, Copy, Debug, PartialEq, Eq)]
237pub enum PosCardinality {
238    One { required: bool },
239    Many,
240    Range { min: usize, max: usize },
241}
242
243/// Positional argument specification.
244pub struct PosSpec<'a, Ctx: ?Sized> {
245    name: &'a str,
246    help: Option<&'a str>,
247    card: PosCardinality,
248    on_value: OnValue<Ctx>,
249    validator: Option<ValueValidator>,
250}
251impl<'a, Ctx: ?Sized> PosSpec<'a, Ctx> {
252    pub const fn new(name: &'a str, cb: OnValue<Ctx>) -> Self {
253        Self {
254            name,
255            help: None,
256            card: PosCardinality::One { required: false },
257            on_value: cb,
258            validator: None,
259        }
260    }
261    // builders
262    #[must_use]
263    pub const fn help(mut self, h: &'a str) -> Self {
264        self.help = Some(h);
265        self
266    }
267    #[must_use]
268    pub const fn required(mut self) -> Self {
269        self.card = PosCardinality::One { required: true };
270        self
271    }
272    #[must_use]
273    pub const fn many(mut self) -> Self {
274        self.card = PosCardinality::Many;
275        self
276    }
277    #[must_use]
278    pub const fn range(mut self, min: usize, max: usize) -> Self {
279        self.card = PosCardinality::Range { min, max };
280        self
281    }
282    #[must_use]
283    pub const fn validator(mut self, v: ValueValidator) -> Self {
284        self.validator = Some(v);
285        self
286    }
287    // getters
288    #[must_use]
289    pub const fn get_name(&self) -> &str {
290        self.name
291    }
292    #[must_use]
293    pub const fn get_help(&self) -> Option<&str> {
294        self.help
295    }
296    #[must_use]
297    pub const fn get_cardinality(&self) -> PosCardinality {
298        self.card
299    }
300    #[must_use]
301    pub const fn is_required(&self) -> bool {
302        matches!(self.card, PosCardinality::One { required: true })
303            || matches!(self.card, PosCardinality::Range { min, .. } if min > 0)
304    }
305    #[must_use]
306    pub const fn is_multiple(&self) -> bool {
307        !matches!(self.card, PosCardinality::One { .. })
308    }
309    #[must_use]
310    pub const fn get_on_value(&self) -> OnValue<Ctx> {
311        self.on_value
312    }
313    #[must_use]
314    pub const fn get_validator(&self) -> Option<ValueValidator> {
315        self.validator
316    }
317}
318
319/// Group declaration.
320pub struct GroupDecl<'a> {
321    pub name: &'a str,
322    pub mode: GroupMode,
323}
324
325/// Command specification.
326pub struct CmdSpec<'a, Ctx: ?Sized> {
327    name: &'a str,
328    help: Option<&'a str>,
329    aliases: Vec<&'a str>,
330    opts: Vec<OptSpec<'a, Ctx>>,
331    positionals: Vec<PosSpec<'a, Ctx>>,
332    subcommands: Vec<CmdSpec<'a, Ctx>>,
333    groups: Vec<GroupDecl<'a>>,
334    validate_cmd: Option<CmdValidator>,
335    handler: Option<CmdHandler<Ctx>>, // leaf command handler
336}
337impl<'a, Ctx: ?Sized> CmdSpec<'a, Ctx> {
338    #[must_use]
339    pub fn new(name: &'a str) -> Self {
340        Self {
341            name,
342            help: None,
343            aliases: Vec::new(),
344            opts: Vec::new(),
345            positionals: Vec::new(),
346            subcommands: Vec::new(),
347            groups: Vec::new(),
348            validate_cmd: None,
349            handler: None,
350        }
351    }
352    // builders
353    #[must_use]
354    pub const fn help(mut self, s: &'a str) -> Self {
355        self.help = Some(s);
356        self
357    }
358    #[must_use]
359    pub fn alias(mut self, a: &'a str) -> Self {
360        self.aliases.push(a);
361        self
362    }
363    #[must_use]
364    pub fn opt(mut self, o: OptSpec<'a, Ctx>) -> Self {
365        self.opts.push(o);
366        self
367    }
368    #[must_use]
369    pub fn pos(mut self, p: PosSpec<'a, Ctx>) -> Self {
370        self.positionals.push(p);
371        self
372    }
373    #[must_use]
374    pub fn subcmd(mut self, c: Self) -> Self {
375        self.subcommands.push(c);
376        self
377    }
378    #[must_use]
379    pub fn group(mut self, name: &'a str, mode: GroupMode) -> Self {
380        self.groups.push(GroupDecl { name, mode });
381        self
382    }
383    /// Set per-command validator (renamed to `validator` for consistency).
384    #[must_use]
385    pub fn validator(mut self, cb: CmdValidator) -> Self {
386        self.validate_cmd = Some(cb);
387        self
388    }
389    /// Set a leaf command handler. Only the **selected leaf** handler is executed.
390    #[must_use]
391    pub fn handler(mut self, cb: CmdHandler<Ctx>) -> Self {
392        self.handler = Some(cb);
393        self
394    }
395    // getters
396    #[must_use]
397    pub const fn get_name(&self) -> &str {
398        self.name
399    }
400    #[must_use]
401    pub const fn get_help(&self) -> Option<&str> {
402        self.help
403    }
404    #[must_use]
405    #[allow(clippy::missing_const_for_fn)]
406    pub fn get_aliases(&self) -> &[&'a str] {
407        &self.aliases
408    }
409    #[must_use]
410    #[allow(clippy::missing_const_for_fn)]
411    pub fn get_opts(&self) -> &[OptSpec<'a, Ctx>] {
412        &self.opts
413    }
414    #[must_use]
415    #[allow(clippy::missing_const_for_fn)]
416    pub fn get_positionals(&self) -> &[PosSpec<'a, Ctx>] {
417        &self.positionals
418    }
419    #[must_use]
420    #[allow(clippy::missing_const_for_fn)]
421    pub fn get_subcommands(&self) -> &[Self] {
422        &self.subcommands
423    }
424    #[must_use]
425    #[allow(clippy::missing_const_for_fn)]
426    pub fn get_groups(&self) -> &[GroupDecl<'a>] {
427        &self.groups
428    }
429    #[must_use]
430    pub fn get_validator(&self) -> Option<CmdValidator> {
431        self.validate_cmd
432    }
433    #[must_use]
434    pub fn get_handler(&self) -> Option<CmdHandler<Ctx>> {
435        self.handler
436    }
437    #[must_use]
438    pub fn find_sub(&self, needle: &str) -> Option<&Self> {
439        self.subcommands.iter().find(|c| c.name == needle || c.aliases.iter().any(|a| *a == needle))
440    }
441}