1 // Std
2 use std::ops::BitOr;
3 #[cfg(feature = "yaml")]
4 use std::str::FromStr;
5 
6 // Third party
7 use bitflags::bitflags;
8 
9 #[doc(hidden)]
10 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
11 pub struct ArgFlags(Flags);
12 
13 impl Default for ArgFlags {
default() -> Self14     fn default() -> Self {
15         Self::empty()
16     }
17 }
18 
19 /// Various settings that apply to arguments and may be set, unset, and checked via getter/setter
20 /// methods [`Arg::setting`], [`Arg::unset_setting`], and [`Arg::is_set`]. This is what the
21 /// [`Arg`] methods which accept a `bool` use internally.
22 ///
23 /// [`Arg`]: crate::Arg
24 /// [`Arg::setting`]: crate::Arg::setting()
25 /// [`Arg::unset_setting`]: crate::Arg::unset_setting()
26 /// [`Arg::is_set`]: crate::Arg::is_set()
27 #[derive(Debug, PartialEq, Copy, Clone)]
28 #[non_exhaustive]
29 pub enum ArgSettings {
30     /// Specifies that an arg must be used
31     Required,
32     /// Allows an arg to accept multiple values
33     MultipleValues,
34     /// Allows an arg to appear multiple times
35     MultipleOccurrences,
36     /// Deprecated, see [`ArgSettings::MultipleOccurrences`] (most likely what you want) and
37     /// [`ArgSettings::MultipleValues`]
38     #[deprecated(
39         since = "3.0.0",
40         note = "Split into `ArgSettings::MultipleOccurrences` (most likely what you want)  and `ArgSettings::MultipleValues`"
41     )]
42     Multiple,
43     /// Forbids an arg from accepting empty values such as `""`
44     ForbidEmptyValues,
45     /// Sets an arg to be global (i.e. exist in all subcommands)
46     Global,
47     /// Hides an arg from the help message
48     Hidden,
49     /// Allows an argument to take a value (such as `--option value`)
50     TakesValue,
51     /// Enables a delimiter to break up arguments `--option val1,val2,val3` becomes three values
52     /// (`val1`, `val2`, and `val3`) instead of the default one (`val1,val2,val3`)
53     UseValueDelimiter,
54     /// Tells an arg to display it's help on the line below the arg itself in the help message
55     NextLineHelp,
56     /// Says that arg *must* use a delimiter to separate values
57     RequireDelimiter,
58     /// Hides the possible values from the help message
59     HidePossibleValues,
60     /// Allows values that start with a hyphen
61     AllowHyphenValues,
62     /// Deprecated, replaced with [`ArgSettings::AllowHyphenValues`]
63     #[deprecated(
64         since = "3.0.0",
65         note = "Replaced with `ArgSettings::AllowHyphenValues`"
66     )]
67     AllowLeadingHyphen,
68     /// Requires that an equals be used to provide a value to an option such as `--option=value`
69     RequireEquals,
70     /// Says that a positional arg will be the last positional, and requires `--` to be accessed.
71     /// It can also be accessed early (i.e. before other positionals) by providing `--`
72     Last,
73     /// Hides the default value from the help message
74     HideDefaultValue,
75     /// Possible values become case insensitive
76     IgnoreCase,
77     /// Deprecated, replaced with [`ArgSettings::IgnoreCase`]
78     #[deprecated(since = "3.0.0", note = "Replaced with `ArgSettings::IgnoreCase`")]
79     CaseInsensitive,
80     /// Hides environment variable arguments from the help message
81     #[cfg(feature = "env")]
82     HideEnv,
83     /// Hides any values currently assigned to ENV variables in the help message (good for sensitive
84     /// information)
85     #[cfg(feature = "env")]
86     HideEnvValues,
87     /// The argument should **not** be shown in short help text
88     HiddenShortHelp,
89     /// The argument should **not** be shown in long help text
90     HiddenLongHelp,
91     /// Specifies that option values that are invalid UTF-8 should *not* be treated as an error.
92     AllowInvalidUtf8,
93     /// Specifies that option should exist on its own.
94     /// Having any other arguments present at runtime is an error.
95     Exclusive,
96 }
97 
98 bitflags! {
99     struct Flags: u32 {
100         const REQUIRED         = 1;
101         const MULTIPLE_OCC     = 1 << 1;
102         const NO_EMPTY_VALS    = 1 << 2;
103         const GLOBAL           = 1 << 3;
104         const HIDDEN           = 1 << 4;
105         const TAKES_VAL        = 1 << 5;
106         const USE_DELIM        = 1 << 6;
107         const NEXT_LINE_HELP   = 1 << 7;
108         const REQ_DELIM        = 1 << 9;
109         const DELIM_NOT_SET    = 1 << 10;
110         const HIDE_POS_VALS    = 1 << 11;
111         const ALLOW_TAC_VALS   = 1 << 12;
112         const REQUIRE_EQUALS   = 1 << 13;
113         const LAST             = 1 << 14;
114         const HIDE_DEFAULT_VAL = 1 << 15;
115         const CASE_INSENSITIVE = 1 << 16;
116         #[cfg(feature = "env")]
117         const HIDE_ENV_VALS    = 1 << 17;
118         const HIDDEN_SHORT_H   = 1 << 18;
119         const HIDDEN_LONG_H    = 1 << 19;
120         const MULTIPLE_VALS    = 1 << 20;
121         const MULTIPLE         = Self::MULTIPLE_OCC.bits | Self::MULTIPLE_VALS.bits;
122         #[cfg(feature = "env")]
123         const HIDE_ENV         = 1 << 21;
124         const UTF8_NONE        = 1 << 22;
125         const EXCLUSIVE        = 1 << 23;
126         const NO_OP            = 0;
127     }
128 }
129 
130 impl_settings! { ArgSettings, ArgFlags,
131     Required => Flags::REQUIRED,
132     MultipleOccurrences => Flags::MULTIPLE_OCC,
133     MultipleValues => Flags::MULTIPLE_VALS,
134     Multiple => Flags::MULTIPLE,
135     ForbidEmptyValues => Flags::NO_EMPTY_VALS,
136     Global => Flags::GLOBAL,
137     Hidden => Flags::HIDDEN,
138     TakesValue => Flags::TAKES_VAL,
139     UseValueDelimiter => Flags::USE_DELIM,
140     NextLineHelp => Flags::NEXT_LINE_HELP,
141     RequireDelimiter => Flags::REQ_DELIM,
142     HidePossibleValues => Flags::HIDE_POS_VALS,
143     AllowHyphenValues => Flags::ALLOW_TAC_VALS,
144     AllowLeadingHyphen => Flags::ALLOW_TAC_VALS,
145     RequireEquals => Flags::REQUIRE_EQUALS,
146     Last => Flags::LAST,
147     IgnoreCase => Flags::CASE_INSENSITIVE,
148     CaseInsensitive => Flags::CASE_INSENSITIVE,
149     #[cfg(feature = "env")]
150     HideEnv => Flags::HIDE_ENV,
151     #[cfg(feature = "env")]
152     HideEnvValues => Flags::HIDE_ENV_VALS,
153     HideDefaultValue => Flags::HIDE_DEFAULT_VAL,
154     HiddenShortHelp => Flags::HIDDEN_SHORT_H,
155     HiddenLongHelp => Flags::HIDDEN_LONG_H,
156     AllowInvalidUtf8 => Flags::UTF8_NONE,
157     Exclusive => Flags::EXCLUSIVE
158 }
159 
160 /// Deprecated in [Issue #3087](https://github.com/clap-rs/clap/issues/3087), maybe [`clap::Parser`][crate::Parser] would fit your use case?
161 #[cfg(feature = "yaml")]
162 impl FromStr for ArgSettings {
163     type Err = String;
from_str(s: &str) -> Result<Self, <Self as FromStr>::Err>164     fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {
165         #[allow(deprecated)]
166         #[allow(unreachable_patterns)]
167         match &*s.to_ascii_lowercase() {
168             "required" => Ok(ArgSettings::Required),
169             "multipleoccurrences" => Ok(ArgSettings::MultipleOccurrences),
170             "multiplevalues" => Ok(ArgSettings::MultipleValues),
171             "multiple" => Ok(ArgSettings::Multiple),
172             "forbidemptyvalues" => Ok(ArgSettings::ForbidEmptyValues),
173             "global" => Ok(ArgSettings::Global),
174             "hidden" => Ok(ArgSettings::Hidden),
175             "takesvalue" => Ok(ArgSettings::TakesValue),
176             "usevaluedelimiter" => Ok(ArgSettings::UseValueDelimiter),
177             "nextlinehelp" => Ok(ArgSettings::NextLineHelp),
178             "requiredelimiter" => Ok(ArgSettings::RequireDelimiter),
179             "hidepossiblevalues" => Ok(ArgSettings::HidePossibleValues),
180             "allowhyphenvalues" => Ok(ArgSettings::AllowHyphenValues),
181             "allowleadinghypyhen" => Ok(ArgSettings::AllowLeadingHyphen),
182             "requireequals" => Ok(ArgSettings::RequireEquals),
183             "last" => Ok(ArgSettings::Last),
184             "ignorecase" => Ok(ArgSettings::IgnoreCase),
185             "caseinsensitive" => Ok(ArgSettings::CaseInsensitive),
186             #[cfg(feature = "env")]
187             "hideenv" => Ok(ArgSettings::HideEnv),
188             #[cfg(feature = "env")]
189             "hideenvvalues" => Ok(ArgSettings::HideEnvValues),
190             "hidedefaultvalue" => Ok(ArgSettings::HideDefaultValue),
191             "hiddenshorthelp" => Ok(ArgSettings::HiddenShortHelp),
192             "hiddenlonghelp" => Ok(ArgSettings::HiddenLongHelp),
193             "allowinvalidutf8" => Ok(ArgSettings::AllowInvalidUtf8),
194             "exclusive" => Ok(ArgSettings::Exclusive),
195             _ => Err(format!("unknown AppSetting: `{}`", s)),
196         }
197     }
198 }
199 
200 #[cfg(test)]
201 mod test {
202     #[test]
203     #[cfg(feature = "yaml")]
arg_settings_fromstr()204     fn arg_settings_fromstr() {
205         use super::ArgSettings;
206 
207         assert_eq!(
208             "allowhyphenvalues".parse::<ArgSettings>().unwrap(),
209             ArgSettings::AllowHyphenValues
210         );
211         assert_eq!(
212             "forbidemptyvalues".parse::<ArgSettings>().unwrap(),
213             ArgSettings::ForbidEmptyValues
214         );
215         assert_eq!(
216             "hidepossiblevalues".parse::<ArgSettings>().unwrap(),
217             ArgSettings::HidePossibleValues
218         );
219         assert_eq!(
220             "hidden".parse::<ArgSettings>().unwrap(),
221             ArgSettings::Hidden
222         );
223         assert_eq!(
224             "nextlinehelp".parse::<ArgSettings>().unwrap(),
225             ArgSettings::NextLineHelp
226         );
227         assert_eq!(
228             "requiredelimiter".parse::<ArgSettings>().unwrap(),
229             ArgSettings::RequireDelimiter
230         );
231         assert_eq!(
232             "required".parse::<ArgSettings>().unwrap(),
233             ArgSettings::Required
234         );
235         assert_eq!(
236             "takesvalue".parse::<ArgSettings>().unwrap(),
237             ArgSettings::TakesValue
238         );
239         assert_eq!(
240             "usevaluedelimiter".parse::<ArgSettings>().unwrap(),
241             ArgSettings::UseValueDelimiter
242         );
243         assert_eq!(
244             "requireequals".parse::<ArgSettings>().unwrap(),
245             ArgSettings::RequireEquals
246         );
247         assert_eq!("last".parse::<ArgSettings>().unwrap(), ArgSettings::Last);
248         assert_eq!(
249             "hidedefaultvalue".parse::<ArgSettings>().unwrap(),
250             ArgSettings::HideDefaultValue
251         );
252         assert_eq!(
253             "ignorecase".parse::<ArgSettings>().unwrap(),
254             ArgSettings::IgnoreCase
255         );
256         #[cfg(feature = "env")]
257         assert_eq!(
258             "hideenv".parse::<ArgSettings>().unwrap(),
259             ArgSettings::HideEnv
260         );
261         #[cfg(feature = "env")]
262         assert_eq!(
263             "hideenvvalues".parse::<ArgSettings>().unwrap(),
264             ArgSettings::HideEnvValues
265         );
266         assert_eq!(
267             "hiddenshorthelp".parse::<ArgSettings>().unwrap(),
268             ArgSettings::HiddenShortHelp
269         );
270         assert_eq!(
271             "hiddenlonghelp".parse::<ArgSettings>().unwrap(),
272             ArgSettings::HiddenLongHelp
273         );
274         assert_eq!(
275             "allowinvalidutf8".parse::<ArgSettings>().unwrap(),
276             ArgSettings::AllowInvalidUtf8
277         );
278         assert_eq!(
279             "exclusive".parse::<ArgSettings>().unwrap(),
280             ArgSettings::Exclusive
281         );
282         assert!("hahahaha".parse::<ArgSettings>().is_err());
283     }
284 }
285