Merge pull request #3438 from epage/flatten

refactor: Flatten directory heirarcy
This commit is contained in:
Ed Page 2022-02-10 11:47:10 -06:00 committed by GitHub
commit c422ed24df
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 183 additions and 183 deletions

View file

@ -1,11 +1,3 @@
#[cfg(debug_assertions)]
mod debug_asserts;
mod settings;
#[cfg(test)]
mod tests;
pub use self::settings::{AppFlags, AppSettings};
// Std
use std::{
collections::HashMap,
@ -23,6 +15,7 @@ use os_str_bytes::RawOsStr;
use yaml_rust::Yaml;
// Internal
use crate::build::app_settings::{AppFlags, AppSettings};
use crate::build::{arg::ArgProvider, Arg, ArgGroup, ArgPredicate};
use crate::error::ErrorKind;
use crate::error::Result as ClapResult;
@ -32,6 +25,9 @@ use crate::parse::{ArgMatcher, ArgMatches, Input, Parser};
use crate::util::{color::ColorChoice, Id, Key};
use crate::{Error, INTERNAL_ERROR_MSG};
#[cfg(debug_assertions)]
use crate::build::debug_asserts::assert_app;
/// Build a command-line interface.
///
/// This includes defining arguments, subcommands, parser behavior, and help output.
@ -2802,14 +2798,14 @@ impl<'help> App<'help> {
self.args._build();
#[cfg(debug_assertions)]
self::debug_asserts::assert_app(self);
assert_app(self);
self.settings.set(AppSettings::Built);
} else {
debug!("App::_build: already built");
}
}
fn _panic_on_missing_help(&self, help_required_globally: bool) {
pub(crate) fn _panic_on_missing_help(&self, help_required_globally: bool) {
if self.is_set(AppSettings::HelpExpected) || help_required_globally {
let args_missing_help: Vec<String> = self
.args
@ -2831,7 +2827,7 @@ impl<'help> App<'help> {
}
#[cfg(debug_assertions)]
fn two_args_of<F>(&self, condition: F) -> Option<(&Arg<'help>, &Arg<'help>)>
pub(crate) fn two_args_of<F>(&self, condition: F) -> Option<(&Arg<'help>, &Arg<'help>)>
where
F: Fn(&Arg) -> bool,
{

View file

@ -1,17 +1,3 @@
mod arg_predicate;
#[cfg(debug_assertions)]
pub mod debug_asserts;
mod possible_value;
mod settings;
#[cfg(test)]
mod tests;
mod value_hint;
pub use self::possible_value::PossibleValue;
pub use self::settings::{ArgFlags, ArgSettings};
pub use self::value_hint::ValueHint;
pub(crate) use arg_predicate::ArgPredicate;
// Std
use std::{
borrow::Cow,
@ -29,17 +15,16 @@ use std::{env, ffi::OsString};
use yaml_rust::Yaml;
// Internal
use crate::{
build::usage_parser::UsageParser,
util::{Id, Key},
INTERNAL_ERROR_MSG,
};
use crate::build::usage_parser::UsageParser;
use crate::build::ArgPredicate;
use crate::util::{Id, Key};
use crate::PossibleValue;
use crate::ValueHint;
use crate::INTERNAL_ERROR_MSG;
use crate::{ArgFlags, ArgSettings};
#[cfg(feature = "regex")]
mod regex;
#[cfg(feature = "regex")]
pub use self::regex::RegexRef;
use crate::build::RegexRef;
/// The abstract representation of a command line argument. Used to set all the options and
/// relationships that define a valid argument for the program.

View file

@ -1,135 +0,0 @@
use crate::{Arg, ValueHint};
pub(crate) fn assert_arg(arg: &Arg) {
debug!("Arg::_debug_asserts:{}", arg.name);
// Self conflict
// TODO: this check should be recursive
assert!(
!arg.blacklist.iter().any(|x| *x == arg.id),
"Argument '{}' cannot conflict with itself",
arg.name,
);
if arg.value_hint != ValueHint::Unknown {
assert!(
arg.is_takes_value_set(),
"Argument '{}' has value hint but takes no value",
arg.name
);
if arg.value_hint == ValueHint::CommandWithArguments {
assert!(
arg.is_multiple_values_set(),
"Argument '{}' uses hint CommandWithArguments and must accept multiple values",
arg.name
)
}
}
if arg.index.is_some() {
assert!(
arg.is_positional(),
"Argument '{}' is a positional argument and can't have short or long name versions",
arg.name
);
}
if arg.is_required_set() {
assert!(
arg.default_vals.is_empty(),
"Argument '{}' is required and can't have a default value",
arg.name
);
}
assert_arg_flags(arg);
assert_defaults(arg, "default_value", arg.default_vals.iter().copied());
assert_defaults(
arg,
"default_missing_value",
arg.default_missing_vals.iter().copied(),
);
assert_defaults(
arg,
"default_value_if",
arg.default_vals_ifs
.iter()
.filter_map(|(_, _, default)| *default),
);
}
fn assert_arg_flags(arg: &Arg) {
macro_rules! checker {
($a:ident requires $($b:ident)|+) => {
if arg.$a() {
let mut s = String::new();
$(
if !arg.$b() {
s.push_str(&format!(" Arg::{} is required when Arg::{} is set.\n", std::stringify!($b), std::stringify!($a)));
}
)+
if !s.is_empty() {
panic!("Argument {:?}\n{}", arg.get_name(), s)
}
}
}
}
checker!(is_forbid_empty_values_set requires is_takes_value_set);
checker!(is_require_value_delimiter_set requires is_takes_value_set);
checker!(is_require_value_delimiter_set requires is_use_value_delimiter_set);
checker!(is_hide_possible_values_set requires is_takes_value_set);
checker!(is_allow_hyphen_values_set requires is_takes_value_set);
checker!(is_require_equals_set requires is_takes_value_set);
checker!(is_last_set requires is_takes_value_set);
checker!(is_hide_default_value_set requires is_takes_value_set);
checker!(is_multiple_values_set requires is_takes_value_set);
checker!(is_ignore_case_set requires is_takes_value_set);
checker!(is_allow_invalid_utf8_set requires is_takes_value_set);
}
fn assert_defaults<'d>(
arg: &Arg,
field: &'static str,
defaults: impl IntoIterator<Item = &'d std::ffi::OsStr>,
) {
for default_os in defaults {
if let Some(default_s) = default_os.to_str() {
if !arg.possible_vals.is_empty() {
assert!(
arg.possible_vals.iter().any(|possible_val| {
possible_val.matches(default_s, arg.is_ignore_case_set())
}),
"Argument `{}`'s {}={} doesn't match possible values",
arg.name,
field,
default_s
);
}
if let Some(validator) = arg.validator.as_ref() {
let mut validator = validator.lock().unwrap();
if let Err(err) = validator(default_s) {
panic!(
"Argument `{}`'s {}={} failed validation: {}",
arg.name, field, default_s, err
);
}
}
}
if let Some(validator) = arg.validator_os.as_ref() {
let mut validator = validator.lock().unwrap();
if let Err(err) = validator(default_os) {
panic!(
"Argument `{}`'s {}={:?} failed validation: {}",
arg.name, field, default_os, err
);
}
}
}
}

View file

@ -1,11 +1,10 @@
use crate::{
build::arg::{debug_asserts::assert_arg, ArgProvider},
mkeymap::KeyType,
util::Id,
App, AppSettings, Arg, ValueHint,
};
use std::cmp::Ordering;
use crate::build::arg::ArgProvider;
use crate::mkeymap::KeyType;
use crate::util::Id;
use crate::{App, AppSettings, Arg, ValueHint};
pub(crate) fn assert_app(app: &App) {
debug!("App::_debug_asserts");
@ -577,3 +576,137 @@ fn _verify_positionals(app: &App) -> bool {
true
}
fn assert_arg(arg: &Arg) {
debug!("Arg::_debug_asserts:{}", arg.name);
// Self conflict
// TODO: this check should be recursive
assert!(
!arg.blacklist.iter().any(|x| *x == arg.id),
"Argument '{}' cannot conflict with itself",
arg.name,
);
if arg.value_hint != ValueHint::Unknown {
assert!(
arg.is_takes_value_set(),
"Argument '{}' has value hint but takes no value",
arg.name
);
if arg.value_hint == ValueHint::CommandWithArguments {
assert!(
arg.is_multiple_values_set(),
"Argument '{}' uses hint CommandWithArguments and must accept multiple values",
arg.name
)
}
}
if arg.index.is_some() {
assert!(
arg.is_positional(),
"Argument '{}' is a positional argument and can't have short or long name versions",
arg.name
);
}
if arg.is_required_set() {
assert!(
arg.default_vals.is_empty(),
"Argument '{}' is required and can't have a default value",
arg.name
);
}
assert_arg_flags(arg);
assert_defaults(arg, "default_value", arg.default_vals.iter().copied());
assert_defaults(
arg,
"default_missing_value",
arg.default_missing_vals.iter().copied(),
);
assert_defaults(
arg,
"default_value_if",
arg.default_vals_ifs
.iter()
.filter_map(|(_, _, default)| *default),
);
}
fn assert_arg_flags(arg: &Arg) {
macro_rules! checker {
($a:ident requires $($b:ident)|+) => {
if arg.$a() {
let mut s = String::new();
$(
if !arg.$b() {
s.push_str(&format!(" Arg::{} is required when Arg::{} is set.\n", std::stringify!($b), std::stringify!($a)));
}
)+
if !s.is_empty() {
panic!("Argument {:?}\n{}", arg.get_name(), s)
}
}
}
}
checker!(is_forbid_empty_values_set requires is_takes_value_set);
checker!(is_require_value_delimiter_set requires is_takes_value_set);
checker!(is_require_value_delimiter_set requires is_use_value_delimiter_set);
checker!(is_hide_possible_values_set requires is_takes_value_set);
checker!(is_allow_hyphen_values_set requires is_takes_value_set);
checker!(is_require_equals_set requires is_takes_value_set);
checker!(is_last_set requires is_takes_value_set);
checker!(is_hide_default_value_set requires is_takes_value_set);
checker!(is_multiple_values_set requires is_takes_value_set);
checker!(is_ignore_case_set requires is_takes_value_set);
checker!(is_allow_invalid_utf8_set requires is_takes_value_set);
}
fn assert_defaults<'d>(
arg: &Arg,
field: &'static str,
defaults: impl IntoIterator<Item = &'d std::ffi::OsStr>,
) {
for default_os in defaults {
if let Some(default_s) = default_os.to_str() {
if !arg.possible_vals.is_empty() {
assert!(
arg.possible_vals.iter().any(|possible_val| {
possible_val.matches(default_s, arg.is_ignore_case_set())
}),
"Argument `{}`'s {}={} doesn't match possible values",
arg.name,
field,
default_s
);
}
if let Some(validator) = arg.validator.as_ref() {
let mut validator = validator.lock().unwrap();
if let Err(err) = validator(default_s) {
panic!(
"Argument `{}`'s {}={} failed validation: {}",
arg.name, field, default_s, err
);
}
}
}
if let Some(validator) = arg.validator_os.as_ref() {
let mut validator = validator.lock().unwrap();
if let Err(err) = validator(default_os) {
panic!(
"Argument `{}`'s {}={:?} failed validation: {}",
arg.name, field, default_os, err
);
}
}
}
}

View file

@ -4,12 +4,33 @@ mod macros;
pub mod app;
pub mod arg;
mod app_settings;
mod arg_group;
mod arg_predicate;
mod arg_settings;
mod possible_value;
mod usage_parser;
mod value_hint;
pub use self::{
app::{App, AppFlags, AppSettings},
arg::{Arg, ArgFlags, ArgSettings, PossibleValue, ValueHint},
arg_group::ArgGroup,
};
pub(crate) use arg::ArgPredicate;
#[cfg(feature = "regex")]
mod regex;
#[cfg(debug_assertions)]
mod debug_asserts;
#[cfg(test)]
mod app_tests;
#[cfg(test)]
mod arg_tests;
pub use app::App;
pub use app_settings::{AppFlags, AppSettings};
pub use arg::Arg;
pub use arg_group::ArgGroup;
pub(crate) use arg_predicate::ArgPredicate;
pub use arg_settings::{ArgFlags, ArgSettings};
pub use possible_value::PossibleValue;
pub use value_hint::ValueHint;
#[cfg(feature = "regex")]
pub use self::regex::RegexRef;

View file

@ -65,7 +65,7 @@ mod macros;
mod derive;
#[cfg(feature = "regex")]
pub use crate::build::arg::RegexRef;
pub use crate::build::RegexRef;
pub mod error;

View file

@ -1,5 +1,5 @@
// Internal
use crate::build::{arg::PossibleValue, App, AppSettings as AS, Arg, ArgPredicate};
use crate::build::{App, AppSettings as AS, Arg, ArgPredicate, PossibleValue};
use crate::error::{Error, Result as ClapResult};
use crate::output::Usage;
use crate::parse::{ArgMatcher, MatchedArg, ParseState, Parser};