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

Allow replacing input on the fly #1697

Merged
merged 1 commit into from
Feb 21, 2020
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 36 additions & 16 deletions src/build/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ mod settings;
pub use self::settings::{AppFlags, AppSettings};

// Std
use std::collections::HashMap;
use std::env;
use std::ffi::OsString;
use std::fmt;
use std::io::{self, BufRead, BufWriter, Write};
use std::iter::Peekable;
use std::path::Path;
use std::process;

Expand All @@ -20,7 +20,7 @@ use crate::mkeymap::MKeyMap;
use crate::output::fmt::ColorWhen;
use crate::output::{Help, Usage};
use crate::parse::errors::Result as ClapResult;
use crate::parse::{ArgMatcher, ArgMatches, Parser};
use crate::parse::{ArgMatcher, ArgMatches, Input, Parser};
use crate::util::{Key, HELP_HASH, VERSION_HASH};
use crate::INTERNAL_ERROR_MSG;

Expand Down Expand Up @@ -110,6 +110,8 @@ pub struct App<'b> {
#[doc(hidden)]
pub subcommands: Vec<App<'b>>,
#[doc(hidden)]
pub replacers: HashMap<&'b str, &'b [&'b str]>,
#[doc(hidden)]
pub groups: Vec<ArgGroup<'b>>,
#[doc(hidden)]
pub help_headings: Vec<Option<&'b str>>,
Expand Down Expand Up @@ -784,6 +786,28 @@ impl<'b> App<'b> {
self
}

/// Replaces an argument to this application with other arguments.
///
/// Below, when the given args are `app install`, they will be changed to `app module install`.
///
/// # Examples
///
/// ```rust
/// # use clap::{App, Arg, };
/// let m = App::new("app")
/// .replace("install", &["module", "install"])
/// .subcommand(App::new("module")
/// .subcommand(App::new("install")))
/// .get_matches_from(vec!["app", "install"]);
///
/// assert!(m.subcommand_matches("module").is_some());
/// assert!(m.subcommand_matches("module").unwrap().subcommand_matches("install").is_some());
/// ```
pub fn replace(mut self, name: &'b str, target: &'b [&'b str]) -> Self {
self.replacers.insert(name, target);
self
}

/// Adds an [`ArgGroup`] to the application. [`ArgGroup`]s are a family of related arguments.
/// By placing them in a logical group, you can build easier requirement and exclusion rules.
/// For instance, you can make an entire [`ArgGroup`] required, meaning that one (and *only*
Expand Down Expand Up @@ -1344,7 +1368,7 @@ impl<'b> App<'b> {
I: IntoIterator<Item = T>,
T: Into<OsString> + Clone,
{
let mut it = itr.into_iter();
let mut it = Input::from(itr.into_iter());
// Get the name of the program (argument 1 of env::args()) and determine the
// actual file
// that was used to execute the program. This is because a program called
Expand All @@ -1353,9 +1377,9 @@ impl<'b> App<'b> {
// to display
// the full path when displaying help messages and such
if !self.settings.is_set(AppSettings::NoBinaryName) {
if let Some(name) = it.next() {
let bn_os = name.into();
let p = Path::new(&*bn_os);
if let Some((name, _)) = it.next(None) {
let p = Path::new(name);

if let Some(f) = p.file_name() {
if let Some(s) = f.to_os_string().to_str() {
if self.bin_name.is_none() {
Expand All @@ -1366,19 +1390,15 @@ impl<'b> App<'b> {
}
}

self._do_parse(&mut it.peekable())
self._do_parse(&mut it)
}
}

// Internally used only
#[doc(hidden)]
impl<'b> App<'b> {
#[doc(hidden)]
fn _do_parse<I, T>(&mut self, it: &mut Peekable<I>) -> ClapResult<ArgMatches>
where
I: Iterator<Item = T>,
T: Into<OsString> + Clone,
{
fn _do_parse(&mut self, it: &mut Input) -> ClapResult<ArgMatches> {
debugln!("App::_do_parse;");
let mut matcher = ArgMatcher::default();

Expand Down Expand Up @@ -1518,7 +1538,7 @@ impl<'b> App<'b> {

pub fn _propagate(&mut self, prop: Propagation) {
macro_rules! propagate_subcmd {
pksunkara marked this conversation as resolved.
Show resolved Hide resolved
($_self:ident, $sc:expr) => {{
($_self:expr, $sc:expr) => {{
// We have to create a new scope in order to tell rustc the borrow of `sc` is
// done and to recursively call this method
{
Expand Down Expand Up @@ -1627,14 +1647,14 @@ impl<'b> App<'b> {
{
a.disp_ord = i;
}
for (i, mut sc) in &mut subcommands_mut!(self)
for (i, mut sc) in &mut subcommands!(self, iter_mut)
.enumerate()
.filter(|&(_, ref sc)| sc.disp_ord == 999)
{
sc.disp_ord = i;
}
}
for sc in subcommands_mut!(self) {
for sc in subcommands!(self, iter_mut) {
sc._derive_display_order();
}
}
Expand Down Expand Up @@ -1703,7 +1723,7 @@ impl<'b> App<'b> {
#[doc(hidden)]
pub fn _build_bin_names(&mut self) {
debugln!("App::_build_bin_names;");
for mut sc in subcommands_mut!(self) {
for mut sc in subcommands!(self, iter_mut) {
debug!("Parser::build_bin_names:iter: bin_name set...");
if sc.bin_name.is_none() {
sdebugln!("No");
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,7 @@ pub use crate::derive::{Clap, FromArgMatches, IntoApp, Subcommand};
pub use crate::output::fmt::Format;
pub use crate::parse::errors::{Error, ErrorKind, Result};
pub use crate::parse::{ArgMatches, OsValues, SubCommand, Values};

#[cfg(feature = "yaml")]
pub use yaml_rust::YamlLoader;

Expand Down
68 changes: 17 additions & 51 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -934,13 +934,6 @@ macro_rules! flags {
};
}

#[allow(unused_macros)]
macro_rules! flags_mut {
($app:expr) => {
$crate::flags!($app, iter_mut)
};
}

#[macro_export]
#[doc(hidden)]
macro_rules! opts {
Expand All @@ -956,46 +949,17 @@ macro_rules! opts {
};
}

#[allow(unused_macros)]
macro_rules! opts_mut {
($app:expr) => {
opts!($app, iter_mut)
};
}

#[macro_export]
#[doc(hidden)]
macro_rules! positionals {
($app:expr) => {
($app:expr, $how:ident) => {{
$app.args
.args
.iter()
.filter(|a| !(a.short.is_some() || a.long.is_some()))
};
}

#[allow(unused_macros)]
macro_rules! positionals_mut {
($app:expr) => {
$app.args
.values_mut()
.$how()
.filter(|a| !(a.short.is_some() || a.long.is_some()))
};
}

#[allow(unused_macros)]
macro_rules! custom_headings_mut {
($app:expr) => {
custom_headings!($app, values_mut)
};
}

macro_rules! subcommands_cloned {
($app:expr, $how:ident) => {
$app.subcommands.$how().cloned()
};
}};
($app:expr) => {
subcommands_cloned!($app, iter)
positionals!($app, iter)
};
}

Expand All @@ -1010,12 +974,6 @@ macro_rules! subcommands {
};
}

macro_rules! subcommands_mut {
($app:expr) => {
subcommands!($app, iter_mut)
};
}

macro_rules! groups_for_arg {
($app:expr, $grp:expr) => {{
debugln!("Parser::groups_for_arg: name={}", $grp);
Expand All @@ -1027,21 +985,26 @@ macro_rules! groups_for_arg {
}

macro_rules! find_subcmd_cloned {
($_self:expr, $sc:expr) => {{
subcommands_cloned!($_self).find(|a| match_alias!(a, $sc, &*a.name))
($app:expr, $sc:expr) => {{
subcommands!($app)
.cloned()
.find(|a| match_alias!(a, $sc, &*a.name))
}};
}

#[macro_export]
#[doc(hidden)]
macro_rules! find_subcmd {
($app:expr, $sc:expr) => {{
subcommands!($app).find(|a| match_alias!(a, $sc, &*a.name))
($app:expr, $sc:expr, $how:ident) => {{
subcommands!($app, $how).find(|a| match_alias!(a, $sc, &*a.name))
}};
($app:expr, $sc:expr) => {
find_subcmd!($app, $sc, iter)
};
}

macro_rules! longs {
($app:expr) => {{
($app:expr, $how:ident) => {{
use crate::mkeymap::KeyType;
$app.args.keys.iter().map(|x| &x.key).filter_map(|a| {
if let KeyType::Long(v) = a {
Expand All @@ -1051,6 +1014,9 @@ macro_rules! longs {
}
})
}};
($app:expr) => {
longs!($app, iter)
};
}

#[macro_export]
Expand Down
2 changes: 1 addition & 1 deletion src/parse/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ mod validator;
pub use self::arg_matcher::ArgMatcher;
pub use self::matches::ArgMatches;
pub use self::matches::{MatchedArg, OsValues, SubCommand, Values};
pub use self::parser::{ParseResult, Parser};
pub use self::parser::{Input, ParseResult, Parser};
pub use self::validator::Validator;
Loading