Implement BitOr for settings

This commit is contained in:
Pavan Kumar Sunkara 2021-09-23 14:57:10 +05:30
parent 5580e8c465
commit 699a3f76d6
12 changed files with 254 additions and 61 deletions

View file

@ -499,9 +499,9 @@ features = ["std", "suggestions", "color"]
#### Opt-in features #### Opt-in features
* **"regex"**: Enables regex validators. (builds dependency `regex`) * **regex**: Enables regex validators. (builds dependency `regex`)
* **"wrap_help"**: Turns on the help text wrapping feature, based on the terminal size. (builds dependency `term-size`) * **wrap_help**: Turns on the help text wrapping feature, based on the terminal size. (builds dependency `term-size`)
* **"yaml"**: Enables building CLIs from YAML documents. (builds dependency `yaml-rust`) * **yaml**: Enables building CLIs from YAML documents. (builds dependency `yaml-rust`)
### More Information ### More Information

View file

@ -4,7 +4,7 @@ mod settings;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
pub use self::settings::AppSettings; pub use self::settings::{AppFlags, AppSettings};
// Std // Std
use std::{ use std::{
@ -24,7 +24,7 @@ use yaml_rust::Yaml;
// Internal // Internal
use crate::{ use crate::{
build::{app::settings::AppFlags, arg::ArgProvider, Arg, ArgGroup, ArgSettings}, build::{arg::ArgProvider, Arg, ArgGroup, ArgSettings},
mkeymap::MKeyMap, mkeymap::MKeyMap,
output::{fmt::Colorizer, Help, HelpWriter, Usage}, output::{fmt::Colorizer, Help, HelpWriter, Usage},
parse::{ArgMatcher, ArgMatches, Input, Parser}, parse::{ArgMatcher, ArgMatches, Input, Parser},
@ -897,9 +897,19 @@ impl<'help> App<'help> {
/// .setting(AppSettings::WaitOnError) /// .setting(AppSettings::WaitOnError)
/// # ; /// # ;
/// ``` /// ```
///
/// ```no_run
/// # use clap::{App, AppSettings};
/// App::new("myprog")
/// .setting(AppSettings::SubcommandRequired | AppSettings::WaitOnError)
/// # ;
/// ```
#[inline] #[inline]
pub fn setting(mut self, setting: AppSettings) -> Self { pub fn setting<F>(mut self, setting: F) -> Self
self.settings.set(setting); where
F: Into<AppFlags>,
{
self.settings.insert(setting.into());
self self
} }
@ -912,12 +922,23 @@ impl<'help> App<'help> {
/// ```no_run /// ```no_run
/// # use clap::{App, AppSettings}; /// # use clap::{App, AppSettings};
/// App::new("myprog") /// App::new("myprog")
/// .unset_setting(AppSettings::ColorAuto) /// .unset_setting(AppSettings::SubcommandRequired)
/// .unset_setting(AppSettings::WaitOnError)
/// # ;
/// ```
///
/// ```no_run
/// # use clap::{App, AppSettings};
/// App::new("myprog")
/// .unset_setting(AppSettings::SubcommandRequired | AppSettings::WaitOnError)
/// # ; /// # ;
/// ``` /// ```
#[inline] #[inline]
pub fn unset_setting(mut self, setting: AppSettings) -> Self { pub fn unset_setting<F>(mut self, setting: F) -> Self
self.settings.unset(setting); where
F: Into<AppFlags>,
{
self.settings.remove(setting.into());
self self
} }
@ -2889,9 +2910,11 @@ impl<'help> From<&'help Yaml> for App<'help> {
} }
a a
} }
"setting" | "settings" => yaml_to_setting!(a, v, setting, "AppSetting", err), "setting" | "settings" => {
yaml_to_setting!(a, v, setting, AppSettings, "AppSetting", err)
}
"global_setting" | "global_settings" => { "global_setting" | "global_settings" => {
yaml_to_setting!(a, v, global_setting, "AppSetting", err) yaml_to_setting!(a, v, global_setting, AppSettings, "AppSetting", err)
} }
"name" => continue, "name" => continue,
s => { s => {

View file

@ -53,15 +53,9 @@ bitflags! {
} }
} }
#[doc(hidden)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub(crate) struct AppFlags(Flags); pub struct AppFlags(Flags);
impl BitOr for AppFlags {
type Output = Self;
fn bitor(self, rhs: Self) -> Self {
AppFlags(self.0 | rhs.0)
}
}
impl Default for AppFlags { impl Default for AppFlags {
fn default() -> Self { fn default() -> Self {

View file

@ -77,7 +77,7 @@ impl<'help> ArgValue<'help> {
/// ArgValue::new("fast") /// ArgValue::new("fast")
/// # ; /// # ;
/// ``` /// ```
/// [hidden]: ArgValue::hide /// [hidden]: ArgValue::hidden
/// [possible value]: crate::Arg::possible_values /// [possible value]: crate::Arg::possible_values
/// [`Arg::hide_possible_values(true)`]: crate::Arg::hide_possible_values() /// [`Arg::hide_possible_values(true)`]: crate::Arg::hide_possible_values()
pub fn new(name: &'help str) -> Self { pub fn new(name: &'help str) -> Self {

View file

@ -7,7 +7,7 @@ mod tests;
mod value_hint; mod value_hint;
pub use self::arg_value::ArgValue; pub use self::arg_value::ArgValue;
pub use self::settings::ArgSettings; pub use self::settings::{ArgFlags, ArgSettings};
pub use self::value_hint::ValueHint; pub use self::value_hint::ValueHint;
// Std // Std
@ -32,7 +32,7 @@ use yaml_rust::Yaml;
// Internal // Internal
use crate::{ use crate::{
build::{arg::settings::ArgFlags, usage_parser::UsageParser}, build::usage_parser::UsageParser,
util::{Id, Key}, util::{Id, Key},
INTERNAL_ERROR_MSG, INTERNAL_ERROR_MSG,
}; };
@ -4635,9 +4635,19 @@ impl<'help> Arg<'help> {
/// .setting(ArgSettings::TakesValue) /// .setting(ArgSettings::TakesValue)
/// # ; /// # ;
/// ``` /// ```
///
/// ```no_run
/// # use clap::{Arg, ArgSettings};
/// Arg::new("config")
/// .setting(ArgSettings::Required | ArgSettings::TakesValue)
/// # ;
/// ```
#[inline] #[inline]
pub fn setting(mut self, setting: ArgSettings) -> Self { pub fn setting<F>(mut self, setting: F) -> Self
self.settings.set(setting); where
F: Into<ArgFlags>,
{
self.settings.insert(setting.into());
self self
} }
@ -4651,11 +4661,22 @@ impl<'help> Arg<'help> {
/// # use clap::{Arg, ArgSettings}; /// # use clap::{Arg, ArgSettings};
/// Arg::new("config") /// Arg::new("config")
/// .unset_setting(ArgSettings::Required) /// .unset_setting(ArgSettings::Required)
/// .unset_setting(ArgSettings::TakesValue)
/// # ;
/// ```
///
/// ```no_run
/// # use clap::{Arg, ArgSettings};
/// Arg::new("config")
/// .unset_setting(ArgSettings::Required | ArgSettings::TakesValue)
/// # ; /// # ;
/// ``` /// ```
#[inline] #[inline]
pub fn unset_setting(mut self, setting: ArgSettings) -> Self { pub fn unset_setting<F>(mut self, setting: F) -> Self
self.settings.unset(setting); where
F: Into<ArgFlags>,
{
self.settings.remove(setting.into());
self self
} }
@ -4889,7 +4910,14 @@ impl<'help> From<&'help Yaml> for Arg<'help> {
} }
} }
"setting" | "settings" => { "setting" | "settings" => {
yaml_to_setting!(a, v, setting, "ArgSetting", format!("arg '{}'", name_str)) yaml_to_setting!(
a,
v,
setting,
ArgSettings,
"ArgSetting",
format!("arg '{}'", name_str)
)
} }
s => { s => {
if !has_metadata { if !has_metadata {

View file

@ -1,5 +1,5 @@
// Std // Std
use std::str::FromStr; use std::{ops::BitOr, str::FromStr};
// Third party // Third party
use bitflags::bitflags; use bitflags::bitflags;
@ -34,8 +34,15 @@ bitflags! {
} }
} }
#[derive(Debug, Clone, Copy)] #[doc(hidden)]
pub(crate) struct ArgFlags(Flags); #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ArgFlags(Flags);
impl Default for ArgFlags {
fn default() -> Self {
Self::empty()
}
}
// @TODO @p6 @internal: Reorder alphabetically // @TODO @p6 @internal: Reorder alphabetically
impl_settings! { ArgSettings, ArgFlags, impl_settings! { ArgSettings, ArgFlags,
@ -64,12 +71,6 @@ impl_settings! { ArgSettings, ArgFlags,
AllowInvalidUtf8("allowinvalidutf8") => Flags::UTF8_NONE AllowInvalidUtf8("allowinvalidutf8") => Flags::UTF8_NONE
} }
impl Default for ArgFlags {
fn default() -> Self {
ArgFlags(Flags::empty())
}
}
/// Various settings that apply to arguments and may be set, unset, and checked via getter/setter /// Various settings that apply to arguments and may be set, unset, and checked via getter/setter
/// methods [`Arg::setting`], [`Arg::unset_setting`], and [`Arg::is_set`]. This is what the /// methods [`Arg::setting`], [`Arg::unset_setting`], and [`Arg::is_set`]. This is what the
/// [`Arg`] methods which accept a `bool` use internally. /// [`Arg`] methods which accept a `bool` use internally.

View file

@ -213,11 +213,11 @@ macro_rules! yaml_to_usize {
#[cfg(feature = "yaml")] #[cfg(feature = "yaml")]
macro_rules! yaml_to_setting { macro_rules! yaml_to_setting {
($a:ident, $v:ident, $c:ident, $t:literal, $n:expr) => {{ ($a:ident, $v:ident, $c:ident, $s:ident, $t:literal, $n:expr) => {{
if let Some(v) = $v.as_vec() { if let Some(v) = $v.as_vec() {
for ys in v { for ys in v {
if let Some(s) = ys.as_str() { if let Some(s) = ys.as_str() {
$a = $a.$c(s.parse().unwrap_or_else(|_| { $a = $a.$c(s.parse::<$s>().unwrap_or_else(|_| {
panic!("Unknown {} '{}' found in YAML file for {}", $t, s, $n) panic!("Unknown {} '{}' found in YAML file for {}", $t, s, $n)
})); }));
} else { } else {
@ -229,7 +229,7 @@ macro_rules! yaml_to_setting {
} }
} else if let Some(v) = $v.as_str() { } else if let Some(v) = $v.as_str() {
$a = $a.$c(v $a = $a.$c(v
.parse() .parse::<$s>()
.unwrap_or_else(|_| panic!("Unknown {} '{}' found in YAML file for {}", $t, v, $n))) .unwrap_or_else(|_| panic!("Unknown {} '{}' found in YAML file for {}", $t, v, $n)))
} else { } else {
panic!("Failed to convert YAML {:?} value to a string", $v); panic!("Failed to convert YAML {:?} value to a string", $v);

View file

@ -8,7 +8,7 @@ mod arg_group;
mod usage_parser; mod usage_parser;
pub use self::{ pub use self::{
app::{App, AppSettings}, app::{App, AppFlags, AppSettings},
arg::{Arg, ArgSettings, ArgValue, ValueHint}, arg::{Arg, ArgFlags, ArgSettings, ArgValue, ValueHint},
arg_group::ArgGroup, arg_group::ArgGroup,
}; };

View file

@ -24,7 +24,9 @@
compile_error!("`std` feature is currently required to build `clap`"); compile_error!("`std` feature is currently required to build `clap`");
pub use crate::{ pub use crate::{
build::{App, AppSettings, Arg, ArgGroup, ArgSettings, ArgValue, ValueHint}, build::{
App, AppFlags, AppSettings, Arg, ArgFlags, ArgGroup, ArgSettings, ArgValue, ValueHint,
},
parse::errors::{Error, ErrorKind, Result}, parse::errors::{Error, ErrorKind, Result},
parse::{ArgMatches, Indices, OsValues, Values}, parse::{ArgMatches, Indices, OsValues, Values},
}; };

View file

@ -532,6 +532,18 @@ macro_rules! impl_settings {
),+ ),+
) => { ) => {
impl $flags { impl $flags {
pub(crate) fn empty() -> Self {
$flags(Flags::empty())
}
pub(crate) fn insert(&mut self, rhs: Self) {
self.0.insert(rhs.0);
}
pub(crate) fn remove(&mut self, rhs: Self) {
self.0.remove(rhs.0);
}
pub(crate) fn set(&mut self, s: $settings) { pub(crate) fn set(&mut self, s: $settings) {
match s { match s {
$( $(
@ -560,6 +572,43 @@ macro_rules! impl_settings {
} }
} }
impl BitOr for $flags {
type Output = Self;
fn bitor(mut self, rhs: Self) -> Self::Output {
self.0.insert(rhs.0);
self
}
}
impl From<$settings> for $flags {
fn from(setting: $settings) -> Self {
let mut flags = $flags::empty();
flags.set(setting);
flags
}
}
impl BitOr<$settings> for $flags {
type Output = Self;
fn bitor(mut self, rhs: $settings) -> Self::Output {
self.set(rhs);
self
}
}
impl BitOr for $settings {
type Output = $flags;
fn bitor(self, rhs: Self) -> Self::Output {
let mut flags = $flags::empty();
flags.set(self);
flags.set(rhs);
flags
}
}
impl FromStr for $settings { impl FromStr for $settings {
type Err = String; type Err = String;
fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> { fn from_str(s: &str) -> Result<Self, <Self as FromStr>::Err> {

View file

@ -148,6 +148,75 @@ SUBCOMMANDS:
Print this message or the help of the given subcommand(s) Print this message or the help of the given subcommand(s)
test"; test";
#[test]
fn setting() {
let m = App::new("setting").setting(AppSettings::AllArgsOverrideSelf);
assert!(m.is_set(AppSettings::AllArgsOverrideSelf));
}
#[test]
fn global_setting() {
let m = App::new("global_setting").global_setting(AppSettings::AllArgsOverrideSelf);
assert!(m.is_set(AppSettings::AllArgsOverrideSelf));
}
#[test]
fn unset_setting() {
let m = App::new("unset_setting").setting(AppSettings::AllArgsOverrideSelf);
assert!(m.is_set(AppSettings::AllArgsOverrideSelf));
let m = m.unset_setting(AppSettings::AllArgsOverrideSelf);
assert!(!m.is_set(AppSettings::AllArgsOverrideSelf), "{:#?}", m);
}
#[test]
fn unset_global_setting() {
let m = App::new("unset_global_setting").global_setting(AppSettings::AllArgsOverrideSelf);
assert!(m.is_set(AppSettings::AllArgsOverrideSelf));
let m = m.unset_global_setting(AppSettings::AllArgsOverrideSelf);
assert!(!m.is_set(AppSettings::AllArgsOverrideSelf), "{:#?}", m);
}
#[test]
fn unset_on_global_setting() {
let m = App::new("unset_on_global_setting").global_setting(AppSettings::AllArgsOverrideSelf);
assert!(m.is_set(AppSettings::AllArgsOverrideSelf));
let m = m.unset_setting(AppSettings::AllArgsOverrideSelf);
assert!(m.is_set(AppSettings::AllArgsOverrideSelf), "{:#?}", m);
}
#[test]
fn setting_bitor() {
let m = App::new("setting_bitor").setting(
AppSettings::InferSubcommands | AppSettings::Hidden | AppSettings::DisableHelpSubcommand,
);
assert!(m.is_set(AppSettings::InferSubcommands));
assert!(m.is_set(AppSettings::Hidden));
assert!(m.is_set(AppSettings::DisableHelpSubcommand));
}
#[test]
fn unset_setting_bitor() {
let m = App::new("unset_setting_bitor")
.setting(AppSettings::InferSubcommands)
.setting(AppSettings::Hidden)
.setting(AppSettings::DisableHelpSubcommand);
assert!(m.is_set(AppSettings::InferSubcommands));
assert!(m.is_set(AppSettings::Hidden));
assert!(m.is_set(AppSettings::DisableHelpSubcommand));
let m = m.unset_setting(
AppSettings::InferSubcommands | AppSettings::Hidden | AppSettings::DisableHelpSubcommand,
);
assert!(!m.is_set(AppSettings::InferSubcommands), "{:#?}", m);
assert!(!m.is_set(AppSettings::Hidden), "{:#?}", m);
assert!(!m.is_set(AppSettings::DisableHelpSubcommand), "{:#?}", m);
}
#[test] #[test]
fn sub_command_negate_required() { fn sub_command_negate_required() {
App::new("sub_command_negate") App::new("sub_command_negate")
@ -583,24 +652,6 @@ fn leading_double_hyphen_trailingvararg() {
); );
} }
#[test]
fn unset_setting() {
let m = App::new("unset_setting").setting(AppSettings::AllArgsOverrideSelf);
assert!(m.is_set(AppSettings::AllArgsOverrideSelf));
let m = m.unset_setting(AppSettings::AllArgsOverrideSelf);
assert!(!m.is_set(AppSettings::AllArgsOverrideSelf));
}
#[test]
fn unset_settings() {
let m = App::new("unset_settings");
assert!(&m.is_set(AppSettings::ColorAuto));
let m = m.unset_global_setting(AppSettings::ColorAuto);
assert!(!m.is_set(AppSettings::ColorAuto), "{:#?}", m);
}
#[test] #[test]
fn disable_help_subcommand() { fn disable_help_subcommand() {
let result = App::new("disablehelp") let result = App::new("disablehelp")

45
tests/arg_settings.rs Normal file
View file

@ -0,0 +1,45 @@
mod utils;
use clap::{Arg, ArgSettings};
#[test]
fn setting() {
let m = Arg::new("setting").setting(ArgSettings::Required);
assert!(m.is_set(ArgSettings::Required));
}
#[test]
fn unset_setting() {
let m = Arg::new("unset_setting").setting(ArgSettings::Required);
assert!(m.is_set(ArgSettings::Required));
let m = m.unset_setting(ArgSettings::Required);
assert!(!m.is_set(ArgSettings::Required), "{:#?}", m);
}
#[test]
fn setting_bitor() {
let m = Arg::new("setting_bitor")
.setting(ArgSettings::Required | ArgSettings::Hidden | ArgSettings::Last);
assert!(m.is_set(ArgSettings::Required));
assert!(m.is_set(ArgSettings::Hidden));
assert!(m.is_set(ArgSettings::Last));
}
#[test]
fn unset_setting_bitor() {
let m = Arg::new("unset_setting_bitor")
.setting(ArgSettings::Required)
.setting(ArgSettings::Hidden)
.setting(ArgSettings::Last);
assert!(m.is_set(ArgSettings::Required));
assert!(m.is_set(ArgSettings::Hidden));
assert!(m.is_set(ArgSettings::Last));
let m = m.unset_setting(ArgSettings::Required | ArgSettings::Hidden | ArgSettings::Last);
assert!(!m.is_set(ArgSettings::Required), "{:#?}", m);
assert!(!m.is_set(ArgSettings::Hidden), "{:#?}", m);
assert!(!m.is_set(ArgSettings::Last), "{:#?}", m);
}