Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Flatten directory heirarcy #3438

Merged
merged 3 commits into from Feb 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 7 additions & 11 deletions src/build/app/mod.rs → src/build/app.rs
@@ -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,
Expand All @@ -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;
Expand All @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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,
{
Expand Down
File renamed without changes.
File renamed without changes.
31 changes: 8 additions & 23 deletions src/build/arg/mod.rs → src/build/arg.rs
@@ -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,
Expand All @@ -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,
};

#[cfg(feature = "regex")]
mod regex;
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")]
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.
Expand Down
135 changes: 0 additions & 135 deletions src/build/arg/debug_asserts.rs

This file was deleted.

File renamed without changes.
File renamed without changes.
File renamed without changes.
145 changes: 139 additions & 6 deletions src/build/app/debug_asserts.rs → src/build/debug_asserts.rs
@@ -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");

Expand Down Expand Up @@ -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
);
}
}
}
}