mirror of
https://github.com/clap-rs/clap
synced 2024-11-10 06:44:16 +00:00
wip refactoring shorts and longs into an arg key
This commit is contained in:
parent
67b890c786
commit
dd441f8f1c
9 changed files with 280 additions and 325 deletions
|
@ -48,6 +48,7 @@ is-it-maintained-open-issues = { repository = "clap-rs/clap" }
|
|||
maintenance = {status = "actively-developed"}
|
||||
|
||||
[dependencies]
|
||||
bstr = "0.2.8"
|
||||
bitflags = "1.0"
|
||||
unicode-width = "0.1.4"
|
||||
textwrap = "0.11"
|
||||
|
|
|
@ -1,37 +1,88 @@
|
|||
struct SwitchData {
|
||||
use crate::INTERNAL_ERR_MSG;
|
||||
|
||||
#[derive(Default)]
|
||||
struct SwitchData<'help> {
|
||||
longs: Vec<Alias<'help>>,
|
||||
short: Option<char>,
|
||||
}
|
||||
|
||||
pub(crate) enum Key<'help> {
|
||||
pub(crate) enum ArgKey<'help> {
|
||||
Unset,
|
||||
Index(usize),
|
||||
Switch(SwitchData),
|
||||
}
|
||||
|
||||
impl<'help> Key {
|
||||
impl Default for ArgKey<'help> {
|
||||
fn default() -> Self {
|
||||
ArgKey::Unset
|
||||
}
|
||||
}
|
||||
|
||||
impl<'help> ArgKey {
|
||||
fn long(&mut self, l: &'help str) {
|
||||
match *self {
|
||||
ArgKey::Unset => *self = ArgKey::Switch(SwitchData::default()),
|
||||
ArgKey::Index => panic!("You cannot add both an index and switch (short or long) to an Arg"),
|
||||
_ => (),
|
||||
}
|
||||
self.switch_mut()
|
||||
.expect(INTERNAL_ERR_MSG)
|
||||
.longs
|
||||
.push(Alias::visible(l.trim_left_matches(|c| c == '-')));
|
||||
}
|
||||
fn longs(&mut self, longs: &[&'help str]) {
|
||||
for l in longs {
|
||||
self.long(l);
|
||||
}
|
||||
}
|
||||
fn hidden_long(&mut self, l: &'help str) {
|
||||
self.switch_mut()
|
||||
.longs
|
||||
.push(Alias::hidden(l.trim_left_matches(|c| c == '-')));
|
||||
}
|
||||
|
||||
fn switch_mut(&mut self) -> &mut SwitchData<'help> {
|
||||
fn hidden_longs(&mut self, longs: &[&'help str]) {
|
||||
for l in longs {
|
||||
self.switch_mut()
|
||||
.longs
|
||||
.push(Alias::hidden(l.trim_left_matches(|c| c == '-')));
|
||||
}
|
||||
}
|
||||
fn has_index(&self) -> bool {
|
||||
use ArgKey::*;
|
||||
match *self {
|
||||
Switch(s) => s,
|
||||
_ => unreachable!(),
|
||||
Index(_) => true,
|
||||
Switch(_) => false,
|
||||
}
|
||||
}
|
||||
fn index(&self) -> Option<&usize>> {
|
||||
match *self {
|
||||
Index(i) => Some(i),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
fn index_mut(&mut self) -> Option<&mut usize> {
|
||||
match *self {
|
||||
Index(i) => Some(i),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn has_switch(&self) -> bool {
|
||||
use Key::*;
|
||||
use ArgKey::*;
|
||||
match *self {
|
||||
Index(_) => false,
|
||||
Switch(_) => true,
|
||||
}
|
||||
}
|
||||
fn switch(&self) -> Option<&SwitchData<'help>> {
|
||||
match *self {
|
||||
Switch(s) => Some(s),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
fn switch_mut(&mut self) -> Option<&mut SwitchData<'help>> {
|
||||
match *self {
|
||||
Switch(s) => Some(s),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ use crate::util::VecMap;
|
|||
use yaml_rust;
|
||||
|
||||
// Internal
|
||||
use crate::build::arg::{ArgKey, SwitchData};
|
||||
use crate::build::UsageParser;
|
||||
use crate::util::Key;
|
||||
#[cfg(any(target_os = "windows", target_arch = "wasm32"))]
|
||||
|
@ -120,15 +121,13 @@ pub struct Arg<'help> {
|
|||
#[doc(hidden)]
|
||||
pub disp_ord: usize,
|
||||
#[doc(hidden)]
|
||||
pub help_heading: Option<&'help str>,
|
||||
#[doc(hidden)]
|
||||
pub global: bool,
|
||||
pub unified_ord: usize,
|
||||
#[doc(hidden)]
|
||||
pub val_names: Option<VecMap<&'help str>>,
|
||||
|
||||
// switch
|
||||
pub key: Key<'help>,
|
||||
pub key: ArgKey<'help>,
|
||||
}
|
||||
|
||||
impl<'help> Arg<'help> {
|
||||
|
@ -281,7 +280,7 @@ impl<'help> Arg<'help> {
|
|||
/// ```
|
||||
/// [`short`]: ./struct.Arg.html#method.short
|
||||
pub fn short(mut self, s: char) -> Self {
|
||||
self.short = Some(s);
|
||||
self.key.short(s);
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -325,10 +324,35 @@ impl<'help> Arg<'help> {
|
|||
self
|
||||
}
|
||||
|
||||
/// Allows adding a [`Arg`] alias, which function as "hidden" arguments that
|
||||
/// Allows adding multiple [`Arg`] longs at once. In the help message each long will be
|
||||
/// displayed normally, with all of it's "aliases" listed in parenthesis.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use clap::{App, Arg};
|
||||
/// let m = App::new("prog")
|
||||
/// .arg(Arg::with_name("test")
|
||||
/// .longs(&["test", "something", "awesome", "cool"]))
|
||||
/// .get_matches_from(vec![
|
||||
/// "prog", "--awesome"
|
||||
/// ]);
|
||||
/// assert!(m.is_present("test"));
|
||||
/// ```
|
||||
/// [`Arg`]: ./struct.Arg.html
|
||||
/// [`App::aliases`]: ./struct.Arg.html#method.aliases
|
||||
pub fn longs(mut self, names: &[&'help str]) -> Self {
|
||||
self.key.longs(names);
|
||||
self
|
||||
}
|
||||
|
||||
/// Allows adding an [`Arg`] hidden [long], which function as "hidden" arguments that
|
||||
/// automatically dispatch as if this argument was used. This is more efficient, and easier
|
||||
/// than creating multiple hidden arguments as one only needs to check for the existence of
|
||||
/// this command, and not all variants.
|
||||
/// this command, and not all dummy variants.
|
||||
///
|
||||
/// These hidden longs will not be displayed or mentioned in the help message as the name
|
||||
/// implies.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
@ -337,7 +361,7 @@ impl<'help> Arg<'help> {
|
|||
/// let m = App::new("prog")
|
||||
/// .arg(Arg::with_name("test")
|
||||
/// .long("test")
|
||||
/// .alias("alias")
|
||||
/// .hidden_long("alias")
|
||||
/// .takes_value(true))
|
||||
/// .get_matches_from(vec![
|
||||
/// "prog", "--alias", "cool"
|
||||
|
@ -346,19 +370,13 @@ impl<'help> Arg<'help> {
|
|||
/// assert_eq!(m.value_of("test"), Some("cool"));
|
||||
/// ```
|
||||
/// [`Arg`]: ./struct.Arg.html
|
||||
pub fn alias<S: Into<&'help str>>(mut self, name: S) -> Self {
|
||||
if let Some(ref mut als) = self.aliases {
|
||||
als.push((name.into(), false));
|
||||
} else {
|
||||
self.aliases = Some(vec![(name.into(), false)]);
|
||||
}
|
||||
/// [long]: ./struct.Arg.html#method.long
|
||||
pub fn hidden_long<S: Into<&'help str>>(mut self, name: S) -> Self {
|
||||
self.key.hidden_long(name);
|
||||
self
|
||||
}
|
||||
|
||||
/// Allows adding [`Arg`] aliases, which function as "hidden" arguments that
|
||||
/// automatically dispatch as if this argument was used. This is more efficient, and easier
|
||||
/// than creating multiple hidden subcommands as one only needs to check for the existence of
|
||||
/// this command, and not all variants.
|
||||
/// Allows adding [`Arg`] multiple [hidden longs] at once.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
@ -367,7 +385,7 @@ impl<'help> Arg<'help> {
|
|||
/// let m = App::new("prog")
|
||||
/// .arg(Arg::with_name("test")
|
||||
/// .long("test")
|
||||
/// .aliases(&["do-stuff", "do-tests", "tests"])
|
||||
/// .hidden_longs(&["do-stuff", "do-tests", "tests"])
|
||||
/// .help("the file to add")
|
||||
/// .required(false))
|
||||
/// .get_matches_from(vec![
|
||||
|
@ -376,73 +394,9 @@ impl<'help> Arg<'help> {
|
|||
/// assert!(m.is_present("test"));
|
||||
/// ```
|
||||
/// [`Arg`]: ./struct.Arg.html
|
||||
pub fn aliases(mut self, names: &[&'help str]) -> Self {
|
||||
if let Some(ref mut als) = self.aliases {
|
||||
for n in names {
|
||||
als.push((n, false));
|
||||
}
|
||||
} else {
|
||||
self.aliases = Some(names.iter().map(|&x| (x, false)).collect());
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Allows adding a [`Arg`] alias that functions exactly like those defined with
|
||||
/// [`Arg::alias`], except that they are visible inside the help message.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use clap::{App, Arg};
|
||||
/// let m = App::new("prog")
|
||||
/// .arg(Arg::with_name("test")
|
||||
/// .visible_alias("something-awesome")
|
||||
/// .long("test")
|
||||
/// .takes_value(true))
|
||||
/// .get_matches_from(vec![
|
||||
/// "prog", "--something-awesome", "coffee"
|
||||
/// ]);
|
||||
/// assert!(m.is_present("test"));
|
||||
/// assert_eq!(m.value_of("test"), Some("coffee"));
|
||||
/// ```
|
||||
/// [`Arg`]: ./struct.Arg.html
|
||||
/// [`App::alias`]: ./struct.Arg.html#method.alias
|
||||
pub fn visible_alias<S: Into<&'help str>>(mut self, name: S) -> Self {
|
||||
if let Some(ref mut als) = self.aliases {
|
||||
als.push((name.into(), true));
|
||||
} else {
|
||||
self.aliases = Some(vec![(name.into(), true)]);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Allows adding multiple [`Arg`] aliases that functions exactly like those defined
|
||||
/// with [`Arg::aliases`], except that they are visible inside the help message.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use clap::{App, Arg};
|
||||
/// let m = App::new("prog")
|
||||
/// .arg(Arg::with_name("test")
|
||||
/// .long("test")
|
||||
/// .visible_aliases(&["something", "awesome", "cool"]))
|
||||
/// .get_matches_from(vec![
|
||||
/// "prog", "--awesome"
|
||||
/// ]);
|
||||
/// assert!(m.is_present("test"));
|
||||
/// ```
|
||||
/// [`Arg`]: ./struct.Arg.html
|
||||
/// [`App::aliases`]: ./struct.Arg.html#method.aliases
|
||||
pub fn visible_aliases(mut self, names: &[&'help str]) -> Self {
|
||||
if let Some(ref mut als) = self.aliases {
|
||||
for n in names {
|
||||
als.push((n, true));
|
||||
}
|
||||
} else {
|
||||
self.aliases = Some(names.iter().map(|n| (*n, true)).collect::<Vec<_>>());
|
||||
}
|
||||
self
|
||||
/// [hidden longs]: ./struct.Arg.html#method.hidden_long
|
||||
pub fn hidden_longs(mut self, names: &[&'help str]) -> Self {
|
||||
self.key.hidden_longs(names): self
|
||||
}
|
||||
|
||||
/// Sets the short help text of the argument that will be displayed to the user when they print
|
||||
|
@ -1529,7 +1483,7 @@ impl<'help> Arg<'help> {
|
|||
/// [`App`]: ./struct.App.html
|
||||
/// [`panic!`]: https://doc.rust-lang.org/std/macro.panic!.html
|
||||
pub fn index(mut self, idx: u64) -> Self {
|
||||
self.index = Some(idx);
|
||||
self.key.index = ArgKey::Index(idx);
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -4045,6 +3999,8 @@ impl<'help> Arg<'help> {
|
|||
#[doc(hidden)]
|
||||
pub fn has_switch(&self) -> bool { self.key.has_switch() }
|
||||
|
||||
pub fn switch(&self) -> &SwitchData { self.key.switch() }
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn longest_filter(&self) -> bool {
|
||||
self.kind() == ArgKind::Opt || self.long.is_some() || self.short.is_none()
|
||||
|
|
|
@ -8,6 +8,6 @@ mod arg_group;
|
|||
mod usage_parser;
|
||||
|
||||
pub use self::app::{App, AppFlags, AppSettings, Propagation};
|
||||
pub use self::arg::{Arg, ArgFlags, ArgSettings};
|
||||
pub use self::arg::{Arg, ArgFlags, ArgKey, ArgSettings};
|
||||
pub use self::arg_group::ArgGroup;
|
||||
pub use self::usage_parser::UsageParser;
|
||||
|
|
|
@ -540,6 +540,7 @@ extern crate ansi_term;
|
|||
extern crate atty;
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
extern crate bstr;
|
||||
#[cfg(feature = "derive")]
|
||||
#[cfg_attr(feature = "derive", allow(unused_imports))]
|
||||
#[cfg_attr(feature = "derive", macro_use)]
|
||||
|
|
313
src/mkeymap.rs
313
src/mkeymap.rs
|
@ -1,119 +1,121 @@
|
|||
// Third Party
|
||||
use bstr::{BStr, BString};
|
||||
|
||||
// Internal
|
||||
use crate::build::Arg;
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use crate::util::Key;
|
||||
|
||||
type Id = u64;
|
||||
|
||||
#[derive(Default, PartialEq, Debug, Clone)]
|
||||
pub struct MKeyMap<'b> {
|
||||
pub keys: Vec<MapKey>,
|
||||
pub args: Vec<Arg<'b>>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct Key {
|
||||
pub key: KeyType,
|
||||
pub struct MapKey {
|
||||
pub key: MapKeyType,
|
||||
pub index: usize,
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Debug, Clone)]
|
||||
pub struct MKeyMap<'b> {
|
||||
pub keys: Vec<Key>,
|
||||
pub args: Vec<Arg<'b>>,
|
||||
built: bool, // mutation isn't possible after being built
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
||||
pub enum KeyType {
|
||||
pub enum MapKeyType {
|
||||
Id(Id),
|
||||
Short(char),
|
||||
Long(OsString),
|
||||
Position(u64),
|
||||
Long(BString),
|
||||
Position(usize),
|
||||
}
|
||||
|
||||
impl KeyType {
|
||||
impl MapKeyType {
|
||||
pub(crate) fn is_position(&self) -> bool {
|
||||
match *self {
|
||||
KeyType::Position(_) => true,
|
||||
MapKeyType::Position(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<&str> for KeyType {
|
||||
fn eq(&self, rhs: &&str) -> bool {
|
||||
impl PartialEq<usize> for MapKeyType {
|
||||
fn eq(&self, rhs: &usize) -> bool {
|
||||
match self {
|
||||
KeyType::Long(ref l) => l == OsStr::new(rhs),
|
||||
MapKeyType::Position(i) => i == rhs,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<char> for KeyType {
|
||||
impl PartialEq<Id> for MapKeyType {
|
||||
fn eq(&self, rhs: &Id) -> bool {
|
||||
match self {
|
||||
MapKeyType::Id(i) => i == rhs,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<str> for MapKeyType {
|
||||
fn eq(&self, rhs: &str) -> bool {
|
||||
match self {
|
||||
MapKeyType::Long(ref l) => l == rhs,
|
||||
MapKeyType::Id(i) => i == rhs.key(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<char> for MapKeyType {
|
||||
fn eq(&self, rhs: &char) -> bool {
|
||||
match self {
|
||||
KeyType::Short(c) => c == rhs,
|
||||
MapKeyType::Short(c) => c == rhs,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<usize> for MapKeyType {
|
||||
fn from(us: usize) -> Self { MapKeyType::Position(us) }
|
||||
}
|
||||
|
||||
impl From<char> for MapKeyType {
|
||||
fn from(c: char) -> Self { MapKeyType::Short(c) }
|
||||
}
|
||||
|
||||
impl From<Id> for MapKeyType {
|
||||
fn from(i: Id) -> Self { MapKeyType::Id(i) }
|
||||
}
|
||||
|
||||
impl<'b> MKeyMap<'b> {
|
||||
pub fn new() -> Self { MKeyMap::default() }
|
||||
//TODO ::from(x), ::with_capacity(n) etc
|
||||
//? set theory ops?
|
||||
|
||||
pub fn contains_long(&self, l: &str) -> bool { self.keys.iter().any(|x| x.key == l) }
|
||||
|
||||
pub fn contains_short(&self, c: char) -> bool { self.keys.iter().any(|x| x.key == c) }
|
||||
|
||||
pub fn insert(&mut self, key: KeyType, value: Arg<'b>) -> usize {
|
||||
let index = self.push(value);
|
||||
self.keys.push(Key { key, index });
|
||||
index
|
||||
}
|
||||
pub fn is_empty(&self) -> bool { self.args.is_empty() }
|
||||
|
||||
pub fn push(&mut self, value: Arg<'b>) -> usize {
|
||||
if self.built {
|
||||
panic!("Cannot add Args to the map after the map is built");
|
||||
}
|
||||
|
||||
let index = self.args.len();
|
||||
self.args.push(value);
|
||||
|
||||
index
|
||||
}
|
||||
//TODO ::push_many([x, y])
|
||||
|
||||
pub fn insert_key(&mut self, key: KeyType, index: usize) {
|
||||
if index >= self.args.len() {
|
||||
panic!("Index out of bounds");
|
||||
pub fn contains<K: Into<MapKeyType>>(&self, k: K) -> bool {
|
||||
let key = k.into();
|
||||
self.keys.iter().any(|x| x.key == key)
|
||||
}
|
||||
|
||||
self.keys.push(Key { key, index });
|
||||
}
|
||||
//TODO ::insert_keyset([Long, Key2])
|
||||
|
||||
// ! Arg mutation functionality
|
||||
|
||||
pub fn get(&self, key: &KeyType) -> Option<&Arg<'b>> {
|
||||
for k in &self.keys {
|
||||
if &k.key == key {
|
||||
return Some(&self.args[k.index]);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
//TODO ::get_first([KeyA, KeyB])
|
||||
|
||||
pub fn get_mut(&mut self, key: &KeyType) -> Option<&mut Arg<'b>> {
|
||||
for k in &self.keys {
|
||||
if &k.key == key {
|
||||
return self.args.get_mut(k.index);
|
||||
}
|
||||
}
|
||||
None
|
||||
pub fn find<K: Into<MapKeyType>>(&self, k: K) -> Option<&Arg<'b>> {
|
||||
let key = k.into();
|
||||
self.keys
|
||||
.iter()
|
||||
.find(|x| x.key == key)
|
||||
.map(|mk| self.args.get(mk.index))
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool { self.keys.is_empty() && self.args.is_empty() }
|
||||
|
||||
pub fn remove_key(&mut self, key: &KeyType) {
|
||||
pub fn remove_key<K: Into<MapKeyType>>(&mut self, k: K) {
|
||||
let key = k.into();
|
||||
let mut idx = None;
|
||||
for (i, k) in self.keys.iter().enumerate() {
|
||||
for k in self.keys.iter() {
|
||||
if &k.key == key {
|
||||
idx = Some(i);
|
||||
idx = Some(k.index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -121,65 +123,9 @@ impl<'b> MKeyMap<'b> {
|
|||
self.keys.swap_remove(idx);
|
||||
}
|
||||
}
|
||||
//TODO ::remove_keys([KeyA, KeyB])
|
||||
|
||||
pub fn insert_key_by_name(&mut self, key: KeyType, name: &str) {
|
||||
let index = self.find_by_name(name);
|
||||
|
||||
self.keys.push(Key { key, index });
|
||||
}
|
||||
|
||||
pub fn _build(&mut self) {
|
||||
self.built = true;
|
||||
|
||||
for (i, arg) in self.args.iter_mut().enumerate() {
|
||||
for k in _get_keys(arg) {
|
||||
self.keys.push(Key { key: k, index: i });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_entries_by_index(&mut self, index: usize) {
|
||||
let short;
|
||||
let positional;
|
||||
let mut longs: Vec<_>;
|
||||
|
||||
{
|
||||
let arg = &self.args[index];
|
||||
short = arg.short.map(KeyType::Short);
|
||||
positional = arg.index.map(KeyType::Position);
|
||||
|
||||
longs = arg
|
||||
.aliases
|
||||
.clone()
|
||||
.map(|v| {
|
||||
v.iter()
|
||||
.map(|(n, _)| KeyType::Long(OsString::from(n)))
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
longs.extend(arg.long.map(|l| KeyType::Long(OsString::from(l))));
|
||||
}
|
||||
|
||||
if let Some(s) = short {
|
||||
self.insert_key(s, index)
|
||||
}
|
||||
if let Some(p) = positional {
|
||||
self.insert_key(p, index)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_by_name(&mut self, name: &str) -> usize {
|
||||
self.args
|
||||
.iter()
|
||||
.position(|x| x.name == name)
|
||||
.expect("No such name found")
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, key: &KeyType) -> Option<Arg<'b>> {
|
||||
if self.built {
|
||||
panic!("Cannot remove args after being built");
|
||||
}
|
||||
pub fn remove<K: Into<MapKeyType>>(&mut self, k: K) -> Option<Arg<'b>> {
|
||||
let key = k.into();
|
||||
let mut idx = None;
|
||||
for k in self.keys.iter() {
|
||||
if &k.key == key {
|
||||
|
@ -189,7 +135,7 @@ impl<'b> MKeyMap<'b> {
|
|||
}
|
||||
if let Some(idx) = idx {
|
||||
let arg = self.args.swap_remove(idx);
|
||||
for key in _get_keys(&arg) {
|
||||
for key in get_keys(&arg) {
|
||||
self.remove_key(&key);
|
||||
}
|
||||
return Some(arg);
|
||||
|
@ -197,22 +143,40 @@ impl<'b> MKeyMap<'b> {
|
|||
None
|
||||
}
|
||||
|
||||
//TODO ::remove_many([KeyA, KeyB])
|
||||
//? probably shouldn't add a possibility for removal?
|
||||
//? or remove by replacement by some dummy object, so the order is preserved
|
||||
|
||||
pub fn remove_by_name(&mut self, _name: Id) -> Option<Arg<'b>> {
|
||||
if self.built {
|
||||
panic!("Cannot remove args after being built");
|
||||
pub fn _build(&mut self) {
|
||||
let mut counter = 0;
|
||||
for (i, arg) in self.args.iter_mut().enumerate() {
|
||||
for k in get_keys(arg) {
|
||||
self.keys.push(MapKey { key: k, index: i });
|
||||
}
|
||||
if arg.arg_key() == ArgKey::Unset {
|
||||
a.index(counter);
|
||||
counter += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_by_name(&self, name: &str) -> Option<&Arg<'b>> {
|
||||
let key: MapKeyType = name.key().into();
|
||||
self.keys
|
||||
.iter()
|
||||
.find(|x| x.key == key)
|
||||
.map(|mk| self.args.get(mk.index))
|
||||
}
|
||||
|
||||
pub fn remove_by_name(&mut self, name: &str) -> Option<Arg<'b>> {
|
||||
let key: MapKeyType = name.key().into();
|
||||
let mut index = None;
|
||||
for (i, arg) in self.args.iter().enumerate() {
|
||||
if arg.id == _name {
|
||||
index = Some(i);
|
||||
for k in self.keys.iter() {
|
||||
if k.key == key {
|
||||
index = Some(k.index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if let Some(i) = index {
|
||||
for key in get_keys(&arg) {
|
||||
self.remove_key(&key);
|
||||
}
|
||||
Some(self.args.swap_remove(i))
|
||||
} else {
|
||||
None
|
||||
|
@ -220,43 +184,36 @@ impl<'b> MKeyMap<'b> {
|
|||
}
|
||||
}
|
||||
|
||||
fn _get_keys(arg: &Arg) -> Vec<KeyType> {
|
||||
fn get_keys(arg: &Arg) -> Vec<MapKeyType> {
|
||||
let mut keys = vec![arg.id.into()];
|
||||
if let Some(index) = arg.index {
|
||||
return vec![KeyType::Position(index)];
|
||||
keys.push(index.into());
|
||||
return keys;
|
||||
}
|
||||
|
||||
let mut keys = vec![];
|
||||
if let Some(c) = arg.short {
|
||||
keys.push(KeyType::Short(c));
|
||||
let sd = arg.switch();
|
||||
if let Some(s) = sd.short() {
|
||||
keys.push(s.into());
|
||||
}
|
||||
if let Some(ref aliases) = arg.aliases {
|
||||
for long in aliases
|
||||
.iter()
|
||||
.map(|(a, _)| KeyType::Long(OsString::from(a)))
|
||||
{
|
||||
keys.push(long);
|
||||
for l in sd.all_longs() {
|
||||
keys.push(l.into());
|
||||
}
|
||||
}
|
||||
if let Some(long) = arg.long {
|
||||
keys.push(KeyType::Long(OsString::from(long)));
|
||||
}
|
||||
|
||||
keys
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use self::KeyType::*;
|
||||
use self::MapKeyType::*;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn get_some_value() {
|
||||
let mut map: MKeyMap = MKeyMap::new();
|
||||
|
||||
map.insert(Long(OsString::from("One")), Arg::with_name("Value1"));
|
||||
map.insert(Long(BString::from("One")), Arg::with_name("Value1"));
|
||||
|
||||
assert_eq!(
|
||||
map.get(&Long(OsString::from("One"))),
|
||||
map.get(&Long(BString::from("One"))),
|
||||
Some(&Arg::with_name("Value1"))
|
||||
);
|
||||
}
|
||||
|
@ -265,10 +222,10 @@ mod tests {
|
|||
fn get_none_value() {
|
||||
let mut map: MKeyMap = MKeyMap::new();
|
||||
|
||||
map.insert(Long(OsString::from("One")), Arg::with_name("Value1"));
|
||||
map.get(&Long(OsString::from("Two")));
|
||||
map.insert(Long(BString::from("One")), Arg::with_name("Value1"));
|
||||
map.get(&Long(BString::from("Two")));
|
||||
|
||||
assert_eq!(map.get(&Long(OsString::from("Two"))), None);
|
||||
assert_eq!(map.get(&Long(BString::from("Two"))), None);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
|
@ -283,10 +240,10 @@ mod tests {
|
|||
fn insert_duplicate_key() {
|
||||
let mut map: MKeyMap = MKeyMap::new();
|
||||
|
||||
map.insert(Long(OsString::from("One")), Arg::with_name("Value1"));
|
||||
map.insert(Long(BString::from("One")), Arg::with_name("Value1"));
|
||||
|
||||
assert_eq!(
|
||||
map.insert(Long(OsString::from("One")), Arg::with_name("Value2")),
|
||||
map.insert(Long(BString::from("One")), Arg::with_name("Value2")),
|
||||
1
|
||||
);
|
||||
}
|
||||
|
@ -296,16 +253,16 @@ mod tests {
|
|||
fn insert_duplicate_value() {
|
||||
let mut map: MKeyMap = MKeyMap::new();
|
||||
|
||||
map.insert(Long(OsString::from("One")), Arg::with_name("Value1"));
|
||||
map.insert(Long(BString::from("One")), Arg::with_name("Value1"));
|
||||
|
||||
let orig_len = map.args.len();
|
||||
|
||||
map.insert(Long(OsString::from("Two")), Arg::with_name("Value1"));
|
||||
map.insert(Long(BString::from("Two")), Arg::with_name("Value1"));
|
||||
|
||||
assert_eq!(map.args.len(), orig_len + 1/* , "Len changed" */);
|
||||
assert_eq!(map.args.len(), orig_len + 1 /* , "Len changed" */);
|
||||
// assert_eq!(
|
||||
// map.get(&Long(OsString::from("One"))),
|
||||
// map.get(&Long(OsString::from("Two")))
|
||||
// map.get(&Long(BString::from("One"))),
|
||||
// map.get(&Long(BString::from("Two")))
|
||||
// );
|
||||
}
|
||||
|
||||
|
@ -321,13 +278,13 @@ mod tests {
|
|||
#[test]
|
||||
fn insert_multiple_keys() {
|
||||
let mut map: MKeyMap = MKeyMap::new();
|
||||
let index = map.insert(Long(OsString::from("One")), Arg::with_name("Value1"));
|
||||
let index = map.insert(Long(BString::from("One")), Arg::with_name("Value1"));
|
||||
|
||||
map.insert_key(Long(OsString::from("Two")), index);
|
||||
map.insert_key(Long(BString::from("Two")), index);
|
||||
|
||||
assert_eq!(
|
||||
map.get(&Long(OsString::from("One"))),
|
||||
map.get(&Long(OsString::from("Two")))
|
||||
map.get(&Long(BString::from("One"))),
|
||||
map.get(&Long(BString::from("Two")))
|
||||
);
|
||||
assert_eq!(map.args.len(), 1);
|
||||
}
|
||||
|
@ -335,13 +292,13 @@ mod tests {
|
|||
// #[test]
|
||||
// fn insert_by_name() {
|
||||
// let mut map: MKeyMap<Arg> = MKeyMap::new();
|
||||
// let index = map.insert(Long(OsString::from("One")), Arg::with_name("Value1"));
|
||||
// let index = map.insert(Long(BString::from("One")), Arg::with_name("Value1"));
|
||||
|
||||
// map.insert_key_by_name(Long(OsString::from("Two")), "Value1");
|
||||
// map.insert_key_by_name(Long(BString::from("Two")), "Value1");
|
||||
|
||||
// assert_eq!(
|
||||
// map.get(Long(OsString::from("One"))),
|
||||
// map.get(Long(OsString::from("Two")))
|
||||
// map.get(Long(BString::from("One"))),
|
||||
// map.get(Long(BString::from("Two")))
|
||||
// );
|
||||
// assert_eq!(map.values.len(), 1);
|
||||
// }
|
||||
|
@ -350,10 +307,10 @@ mod tests {
|
|||
fn get_mutable() {
|
||||
let mut map: MKeyMap = MKeyMap::new();
|
||||
|
||||
map.insert(Long(OsString::from("One")), Arg::with_name("Value1"));
|
||||
map.insert(Long(BString::from("One")), Arg::with_name("Value1"));
|
||||
|
||||
assert_eq!(
|
||||
map.get_mut(&Long(OsString::from("One"))),
|
||||
map.get_mut(&Long(BString::from("One"))),
|
||||
Some(&mut Arg::with_name("Value1"))
|
||||
);
|
||||
}
|
||||
|
@ -361,10 +318,10 @@ mod tests {
|
|||
#[test]
|
||||
fn remove_key() {
|
||||
let mut map: MKeyMap = MKeyMap::new();
|
||||
let index = map.insert(Long(OsString::from("One")), Arg::with_name("Value1"));
|
||||
let index = map.insert(Long(BString::from("One")), Arg::with_name("Value1"));
|
||||
|
||||
map.insert_key(Long(OsString::from("Two")), index);
|
||||
map.remove_key(&Long(OsString::from("One")));
|
||||
map.insert_key(Long(BString::from("Two")), index);
|
||||
map.remove_key(&Long(BString::from("One")));
|
||||
|
||||
assert_eq!(map.keys.len(), 1);
|
||||
assert_eq!(map.args.len(), 1);
|
||||
|
|
|
@ -48,7 +48,12 @@ pub struct Help<'b, 'c, 'd, 'w> {
|
|||
// Public Functions
|
||||
impl<'b, 'c, 'd, 'w> Help<'b, 'c, 'd, 'w> {
|
||||
/// Create a new `Help` instance.
|
||||
pub fn new(w: &'w mut dyn Write, parser: &'d Parser<'b, 'c>, use_long: bool, stderr: bool) -> Self {
|
||||
pub fn new(
|
||||
w: &'w mut dyn Write,
|
||||
parser: &'d Parser<'b, 'c>,
|
||||
use_long: bool,
|
||||
stderr: bool,
|
||||
) -> Self {
|
||||
debugln!("Help::new;");
|
||||
let term_w = match parser.app.term_w {
|
||||
Some(0) => usize::MAX,
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
// Std
|
||||
use std::cell::Cell;
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::io::{self, BufWriter, Write};
|
||||
use std::iter::Peekable;
|
||||
use std::mem;
|
||||
#[cfg(all(
|
||||
feature = "debug",
|
||||
not(any(target_os = "windows", target_arch = "wasm32"))
|
||||
))]
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
|
||||
// Third Party
|
||||
use bstr::{BStr, BString};
|
||||
|
||||
// Internal
|
||||
use crate::build::app::Propagation;
|
||||
|
@ -23,9 +20,7 @@ use crate::parse::errors::Result as ClapResult;
|
|||
use crate::parse::features::suggestions;
|
||||
use crate::parse::Validator;
|
||||
use crate::parse::{ArgMatcher, SubCommand};
|
||||
use crate::util::{self, ChildGraph, Key, OsStrExt2, EMPTY_HASH};
|
||||
#[cfg(all(feature = "debug", any(target_os = "windows", target_arch = "wasm32")))]
|
||||
use crate::util::OsStrExt3;
|
||||
use crate::util::{self, ChildGraph, Key, EMPTY_HASH};
|
||||
use crate::INTERNAL_ERROR_MSG;
|
||||
use crate::INVALID_UTF8;
|
||||
|
||||
|
@ -151,16 +146,12 @@ where
|
|||
|
||||
// We can't pass the closure (it.next()) to the macro directly because each call to
|
||||
// find() (iterator, not macro) gets called repeatedly.
|
||||
let last = self
|
||||
.app
|
||||
.args
|
||||
.get(&KeyType::Position(highest_idx))
|
||||
.expect(INTERNAL_ERROR_MSG);
|
||||
let last = self.app.args.find(highest_idx).expect(INTERNAL_ERROR_MSG);
|
||||
|
||||
let second_to_last = self
|
||||
.app
|
||||
.args
|
||||
.get(&KeyType::Position(highest_idx - 1))
|
||||
.find(highest_idx - 1)
|
||||
.expect(INTERNAL_ERROR_MSG);
|
||||
|
||||
// Either the final positional is required
|
||||
|
@ -364,7 +355,7 @@ where
|
|||
'b: 'c,
|
||||
{
|
||||
// The actual parsing function
|
||||
#[allow(clippy::cyclomatic_complexity)]
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
pub fn get_matches_with<I, T>(
|
||||
&mut self,
|
||||
matcher: &mut ArgMatcher,
|
||||
|
@ -372,7 +363,7 @@ where
|
|||
) -> ClapResult<()>
|
||||
where
|
||||
I: Iterator<Item = T>,
|
||||
T: Into<OsString> + Clone,
|
||||
T: Into<BString> + Clone,
|
||||
{
|
||||
debugln!("Parser::get_matches_with;");
|
||||
// Verify all positional assertions pass
|
||||
|
@ -437,9 +428,7 @@ where
|
|||
needs_val_of
|
||||
);
|
||||
match needs_val_of {
|
||||
ParseResult::Flag | ParseResult::Opt(..) | ParseResult::ValuesDone => {
|
||||
continue
|
||||
}
|
||||
ParseResult::Flag | ParseResult::Opt(..) | ParseResult::ValuesDone => continue,
|
||||
_ => (),
|
||||
}
|
||||
} else if arg_os.starts_with(b"-") && arg_os.len() != 1 {
|
||||
|
@ -465,9 +454,7 @@ where
|
|||
));
|
||||
}
|
||||
}
|
||||
ParseResult::Opt(..) | ParseResult::Flag | ParseResult::ValuesDone => {
|
||||
continue
|
||||
}
|
||||
ParseResult::Opt(..) | ParseResult::Flag | ParseResult::ValuesDone => continue,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
@ -716,16 +703,11 @@ where
|
|||
}
|
||||
|
||||
// Checks if the arg matches a subcommand name, or any of it's aliases (if defined)
|
||||
fn possible_subcommand(&self, arg_os: &OsStr) -> (bool, Option<&str>) {
|
||||
fn possible_subcommand(&self, arg_os: &BStr) -> (bool, Option<&str>) {
|
||||
debugln!("Parser::possible_subcommand: arg={:?}", arg_os);
|
||||
fn starts(h: &str, n: &OsStr) -> bool {
|
||||
#[cfg(target_os = "windows")]
|
||||
use crate::util::OsStrExt3;
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
|
||||
fn starts(h: &str, n: &BStr) -> bool {
|
||||
let n_bytes = n.as_bytes();
|
||||
let h_bytes = OsStr::new(h).as_bytes();
|
||||
let h_bytes = BStr::from(h).as_bytes();
|
||||
|
||||
h_bytes.starts_with(n_bytes)
|
||||
}
|
||||
|
@ -752,10 +734,10 @@ where
|
|||
fn parse_help_subcommand<I, T>(&self, it: &mut I) -> ClapResult<ParseResult>
|
||||
where
|
||||
I: Iterator<Item = T>,
|
||||
T: Into<OsString>,
|
||||
T: Into<BString>,
|
||||
{
|
||||
debugln!("Parser::parse_help_subcommand;");
|
||||
let cmds: Vec<OsString> = it.map(Into::into).collect();
|
||||
let cmds: Vec<BString> = it.map(Into::into).collect();
|
||||
let mut help_help = false;
|
||||
let mut bin_name = self.app.bin_name.as_ref().unwrap_or(&self.app.name).clone();
|
||||
let mut sc = {
|
||||
|
@ -813,7 +795,7 @@ where
|
|||
|
||||
// allow wrong self convention due to self.valid_neg_num = true and it's a private method
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
fn is_new_arg(&mut self, arg_os: &OsStr, needs_val_of: ParseResult) -> bool {
|
||||
fn is_new_arg(&mut self, arg_os: &BStr, needs_val_of: ParseResult) -> bool {
|
||||
debugln!("Parser::is_new_arg:{:?}:{:?}", arg_os, needs_val_of);
|
||||
let app_wide_settings = if self.is_set(AS::AllowLeadingHyphen) {
|
||||
true
|
||||
|
@ -874,7 +856,7 @@ where
|
|||
) -> ClapResult<()>
|
||||
where
|
||||
I: Iterator<Item = T>,
|
||||
T: Into<OsString> + Clone,
|
||||
T: Into<BString> + Clone,
|
||||
{
|
||||
use std::fmt::Write;
|
||||
debugln!("Parser::parse_subcommand;");
|
||||
|
@ -933,7 +915,7 @@ where
|
|||
|
||||
// Retrieves the names of all args the user has supplied thus far, except required ones
|
||||
// because those will be listed in self.required
|
||||
fn check_for_help_and_version_str(&self, arg: &OsStr) -> ClapResult<()> {
|
||||
fn check_for_help_and_version_str(&self, arg: &BStr) -> ClapResult<()> {
|
||||
debugln!("Parser::check_for_help_and_version_str;");
|
||||
debug!(
|
||||
"Parser::check_for_help_and_version_str: Checking if --{} is help or version...",
|
||||
|
@ -964,7 +946,7 @@ where
|
|||
// Needs to use app.settings.is_set instead of just is_set() because is_set() checks
|
||||
// both global and local settings, we only want to check local
|
||||
if let Some(help) = self.app.find(util::HELP_HASH) {
|
||||
if let Some(h) = help.short {
|
||||
if let Some(h) = help.switch().short() {
|
||||
if arg == h && !self.app.settings.is_set(AS::NoAutoHelp) {
|
||||
sdebugln!("Help");
|
||||
return Err(self.help_err(false));
|
||||
|
@ -972,7 +954,7 @@ where
|
|||
}
|
||||
}
|
||||
if let Some(version) = self.app.find(util::VERSION_HASH) {
|
||||
if let Some(v) = version.short {
|
||||
if let Some(v) = version.switch().short() {
|
||||
if arg == v && !self.app.settings.is_set(AS::NoAutoVersion) {
|
||||
sdebugln!("Version");
|
||||
return Err(self.version_err(false));
|
||||
|
@ -1003,7 +985,7 @@ where
|
|||
fn parse_long_arg(
|
||||
&mut self,
|
||||
matcher: &mut ArgMatcher,
|
||||
full_arg: &OsStr,
|
||||
full_arg: &BStr,
|
||||
) -> ClapResult<ParseResult> {
|
||||
// maybe here lifetime should be 'a
|
||||
debugln!("Parser::parse_long_arg;");
|
||||
|
@ -1053,7 +1035,7 @@ where
|
|||
fn parse_short_arg(
|
||||
&mut self,
|
||||
matcher: &mut ArgMatcher,
|
||||
full_arg: &OsStr,
|
||||
full_arg: &BStr,
|
||||
) -> ClapResult<ParseResult> {
|
||||
debugln!("Parser::parse_short_arg: full_arg={:?}", full_arg);
|
||||
let arg_os = full_arg.trim_left_matches(b'-');
|
||||
|
@ -1140,7 +1122,7 @@ where
|
|||
|
||||
fn parse_opt(
|
||||
&self,
|
||||
val: Option<&OsStr>,
|
||||
val: Option<&BStr>,
|
||||
opt: &Arg<'b>,
|
||||
had_eq: bool,
|
||||
matcher: &mut ArgMatcher,
|
||||
|
@ -1206,7 +1188,7 @@ where
|
|||
fn add_val_to_arg(
|
||||
&self,
|
||||
arg: &Arg<'b>,
|
||||
val: &OsStr,
|
||||
val: &BStr,
|
||||
matcher: &mut ArgMatcher,
|
||||
) -> ClapResult<ParseResult> {
|
||||
debugln!("Parser::add_val_to_arg; arg={}, val={:?}", arg.name, val);
|
||||
|
@ -1243,7 +1225,7 @@ where
|
|||
fn add_single_val_to_arg(
|
||||
&self,
|
||||
arg: &Arg<'b>,
|
||||
v: &OsStr,
|
||||
v: &BStr,
|
||||
matcher: &mut ArgMatcher,
|
||||
) -> ClapResult<ParseResult> {
|
||||
debugln!("Parser::add_single_val_to_arg;");
|
||||
|
@ -1377,7 +1359,7 @@ where
|
|||
"Parser::add_defaults:iter:{}: has no user defined vals",
|
||||
$a.name
|
||||
);
|
||||
$_self.add_val_to_arg($a, OsStr::new(val), $m)?;
|
||||
$_self.add_val_to_arg($a, BStr::from(val), $m)?;
|
||||
} else if $m.get($a.id).is_some() {
|
||||
debugln!(
|
||||
"Parser::add_defaults:iter:{}: has user defined vals",
|
||||
|
@ -1386,7 +1368,7 @@ where
|
|||
} else {
|
||||
debugln!("Parser::add_defaults:iter:{}: wasn't used", $a.name);
|
||||
|
||||
$_self.add_val_to_arg($a, OsStr::new(val), $m)?;
|
||||
$_self.add_val_to_arg($a, BStr::from(val), $m)?;
|
||||
}
|
||||
} else {
|
||||
debugln!(
|
||||
|
@ -1411,7 +1393,7 @@ where
|
|||
false
|
||||
};
|
||||
if add {
|
||||
$_self.add_val_to_arg($a, OsStr::new(default), $m)?;
|
||||
$_self.add_val_to_arg($a, BStr::from(default), $m)?;
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
|
@ -1443,7 +1425,7 @@ where
|
|||
for a in self.app.args.args.iter() {
|
||||
if let Some(ref val) = a.env {
|
||||
if let Some(ref val) = val.1 {
|
||||
self.add_val_to_arg(a, OsStr::new(val), matcher)?;
|
||||
self.add_val_to_arg(a, BStr::from(val), matcher)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1480,7 +1462,7 @@ where
|
|||
|
||||
// Add the arg to the matches to build a proper usage string
|
||||
if let Some(ref name) = suffix.1 {
|
||||
if let Some(opt) = self.app.args.get(&KeyType::Long(OsString::from(name))) {
|
||||
if let Some(opt) = self.app.args.get(&KeyType::Long(BString::from(name))) {
|
||||
for g in groups_for_arg!(self.app, opt.id) {
|
||||
matcher.inc_occurrence_of(g);
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
* Instead of adding arg with long `--help` or `--version` you can use `App::mut_arg` to override things
|
||||
* Caution, must fully override
|
||||
* No longer forces auto-handle of help/ver however if still desired `AppSettings::NoAuto{Help,Version}`
|
||||
* `Arg::alias` -> `Arg::hidden_long`
|
||||
* `Arg::visible_alias` -> `Arg::long`
|
||||
|
||||
# How to Upgrade
|
||||
|
||||
|
|
Loading…
Reference in a new issue