Skip to main content

usage/spec/
unknown_flags.rs

1use serde::Serialize;
2use strum::{Display as StrumDisplay, EnumString};
3
4/// What to do with a token that looks like a flag but names no declared flag.
5///
6/// The default is [`UnknownFlags::Value`], which is where this parser parts
7/// company with clap, argparse, commander, oclif v2+, and POSIX `getopt` — all of
8/// which reject the token. The reason is that those parse *their own* argv, where
9/// a dash-word can only be a flag or a typo, while a usage spec is also used to
10/// parse things whose flags it does not own:
11///
12/// - a shell script run through `usage exec`, forwarding options to a tool it wraps
13/// - a task's arguments, where the task script is the authority on what it accepts
14/// - a completion, asked about a command line that is still being typed
15///
16/// In all three, a dash-word the spec has not heard of is far more likely to be
17/// data in transit than a mistake, and rejecting it would break the wrapper for
18/// everyone who did not enumerate the flags of the program behind it.
19///
20/// The cost is real and worth stating: a misspelled `--hekp` becomes an argument
21/// instead of an error, and whether it does depends on whether a positional is
22/// free to take it. A CLI that owns all of its flags — as opposed to forwarding
23/// them — should say [`UnknownFlags::Error`] and get the stricter reading.
24#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, EnumString, StrumDisplay, Serialize)]
25#[strum(serialize_all = "snake_case")]
26#[serde(rename_all = "snake_case")]
27pub enum UnknownFlags {
28    /// Offer the token to the positional arguments, like any other word. If none
29    /// can take it, it is an unexpected argument — the same error an extra word
30    /// would produce.
31    #[default]
32    Value,
33    /// Reject the token. A CLI whose flags are all its own gets typo detection
34    /// this way, at the price of needing `--` to pass a value that begins with a
35    /// dash.
36    Error,
37}
38
39impl UnknownFlags {
40    pub fn as_str(&self) -> &'static str {
41        match self {
42            UnknownFlags::Value => "value",
43            UnknownFlags::Error => "error",
44        }
45    }
46}
47
48/// The values a spec may use, for error messages.
49pub(crate) const UNKNOWN_FLAGS_VALUES: &str = "value, error";