mirror of
https://github.com/clap-rs/clap
synced 2025-03-04 15:27:16 +00:00
perf(parser): Don't allocate on every call
This commit is contained in:
parent
36236957e4
commit
c7bd8c891d
3 changed files with 68 additions and 23 deletions
|
@ -139,6 +139,7 @@ impl ArgMatcher {
|
|||
id, source
|
||||
);
|
||||
let ma = self.entry(id).or_insert(MatchedArg::new_arg(arg));
|
||||
debug_assert_eq!(ma.type_id(), Some(arg.get_value_parser().type_id()));
|
||||
ma.set_source(source);
|
||||
}
|
||||
|
||||
|
@ -148,6 +149,7 @@ impl ArgMatcher {
|
|||
id, source
|
||||
);
|
||||
let ma = self.entry(id).or_insert(MatchedArg::new_group());
|
||||
debug_assert_eq!(ma.type_id(), None);
|
||||
ma.set_source(source);
|
||||
}
|
||||
|
||||
|
@ -155,6 +157,7 @@ impl ArgMatcher {
|
|||
let id = &arg.id;
|
||||
debug!("ArgMatcher::start_occurrence_of_arg: id={:?}", id);
|
||||
let ma = self.entry(id).or_insert(MatchedArg::new_arg(arg));
|
||||
debug_assert_eq!(ma.type_id(), Some(arg.get_value_parser().type_id()));
|
||||
ma.set_source(ValueSource::CommandLine);
|
||||
ma.inc_occurrences();
|
||||
}
|
||||
|
@ -162,6 +165,7 @@ impl ArgMatcher {
|
|||
pub(crate) fn start_occurrence_of_group(&mut self, id: &Id) {
|
||||
debug!("ArgMatcher::start_occurrence_of_group: id={:?}", id);
|
||||
let ma = self.entry(id).or_insert(MatchedArg::new_group());
|
||||
debug_assert_eq!(ma.type_id(), None);
|
||||
ma.set_source(ValueSource::CommandLine);
|
||||
ma.inc_occurrences();
|
||||
}
|
||||
|
@ -170,6 +174,14 @@ impl ArgMatcher {
|
|||
let id = &Id::empty_hash();
|
||||
debug!("ArgMatcher::start_occurrence_of_external: id={:?}", id,);
|
||||
let ma = self.entry(id).or_insert(MatchedArg::new_external(cmd));
|
||||
debug_assert_eq!(
|
||||
ma.type_id(),
|
||||
Some(
|
||||
cmd.get_external_subcommand_value_parser()
|
||||
.expect(INTERNAL_ERROR_MSG)
|
||||
.type_id()
|
||||
)
|
||||
);
|
||||
ma.set_source(ValueSource::CommandLine);
|
||||
ma.inc_occurrences();
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ use crate::parser::MatchesError;
|
|||
use crate::parser::ValueSource;
|
||||
use crate::util::{Id, Key};
|
||||
use crate::Error;
|
||||
use crate::INTERNAL_ERROR_MSG;
|
||||
|
||||
/// Container for parse results.
|
||||
///
|
||||
|
@ -115,19 +116,17 @@ impl ArgMatches {
|
|||
/// [`occurrences_of`]: crate::ArgMatches::occurrences_of()
|
||||
pub fn get_one<T: 'static>(&self, name: &str) -> Result<Option<&T>, MatchesError> {
|
||||
let id = Id::from(name);
|
||||
let value = match self.try_get_arg(&id)?.and_then(|a| a.first()) {
|
||||
let arg = self.try_get_arg_t::<T>(&id)?;
|
||||
let value = match arg.and_then(|a| a.first()) {
|
||||
Some(value) => value,
|
||||
None => {
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
value
|
||||
Ok(value
|
||||
.downcast_ref::<T>()
|
||||
.map(Some)
|
||||
.ok_or_else(|| MatchesError::Downcast {
|
||||
actual: value.type_id(),
|
||||
expected: AnyValueId::of::<T>(),
|
||||
})
|
||||
.expect(INTERNAL_ERROR_MSG)) // enforced by `try_get_arg_t`
|
||||
}
|
||||
|
||||
/// Iterate over [values] of a specific option or positional argument.
|
||||
|
@ -164,22 +163,15 @@ impl ArgMatches {
|
|||
name: &str,
|
||||
) -> Result<Option<impl Iterator<Item = &T>>, MatchesError> {
|
||||
let id = Id::from(name);
|
||||
let values = match self.try_get_arg(&id)? {
|
||||
Some(values) => values.vals_flatten(),
|
||||
None => {
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
// HACK: Track the type id and report errors even when there are no values
|
||||
let values: Result<Vec<&T>, MatchesError> = values
|
||||
.map(|v| {
|
||||
v.downcast_ref::<T>().ok_or_else(|| MatchesError::Downcast {
|
||||
actual: v.type_id(),
|
||||
expected: AnyValueId::of::<T>(),
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
Ok(Some(values?.into_iter()))
|
||||
match self.try_get_arg_t::<T>(&id)? {
|
||||
Some(values) => Ok(Some(
|
||||
values
|
||||
.vals_flatten()
|
||||
// enforced by `try_get_arg_t`
|
||||
.map(|v| v.downcast_ref::<T>().expect(INTERNAL_ERROR_MSG)),
|
||||
)),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterate over the original argument values.
|
||||
|
@ -1288,6 +1280,31 @@ impl ArgMatches {
|
|||
Ok(self.args.get(arg))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_get_arg_t<T: 'static>(&self, arg: &Id) -> Result<Option<&MatchedArg>, MatchesError> {
|
||||
let expected = AnyValueId::of::<T>();
|
||||
|
||||
let arg = match self.try_get_arg(arg)? {
|
||||
Some(arg) => arg,
|
||||
None => {
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
let actual = arg
|
||||
.type_id()
|
||||
.or_else(|| {
|
||||
arg.vals_flatten()
|
||||
.map(|v| v.type_id())
|
||||
.find(|actual| *actual != expected)
|
||||
})
|
||||
.unwrap_or(expected);
|
||||
if expected == actual {
|
||||
Ok(Some(arg))
|
||||
} else {
|
||||
Err(MatchesError::Downcast { actual, expected })
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg_attr(debug_assertions, track_caller)]
|
||||
fn get_arg(&self, arg: &Id) -> Option<&MatchedArg> {
|
||||
|
|
|
@ -7,6 +7,7 @@ use std::{
|
|||
|
||||
use crate::builder::ArgPredicate;
|
||||
use crate::parser::AnyValue;
|
||||
use crate::parser::AnyValueId;
|
||||
use crate::parser::ValueSource;
|
||||
use crate::util::eq_ignore_case;
|
||||
use crate::INTERNAL_ERROR_MSG;
|
||||
|
@ -16,6 +17,7 @@ pub(crate) struct MatchedArg {
|
|||
occurs: u64,
|
||||
source: Option<ValueSource>,
|
||||
indices: Vec<usize>,
|
||||
type_id: Option<AnyValueId>,
|
||||
vals: Vec<Vec<AnyValue>>,
|
||||
raw_vals: Vec<Vec<OsString>>,
|
||||
ignore_case: bool,
|
||||
|
@ -28,6 +30,7 @@ impl MatchedArg {
|
|||
occurs: 0,
|
||||
source: None,
|
||||
indices: Vec::new(),
|
||||
type_id: Some(arg.get_value_parser().type_id()),
|
||||
vals: Vec::new(),
|
||||
raw_vals: Vec::new(),
|
||||
ignore_case,
|
||||
|
@ -40,18 +43,24 @@ impl MatchedArg {
|
|||
occurs: 0,
|
||||
source: None,
|
||||
indices: Vec::new(),
|
||||
type_id: None,
|
||||
vals: Vec::new(),
|
||||
raw_vals: Vec::new(),
|
||||
ignore_case,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new_external(_cmd: &crate::Command) -> Self {
|
||||
pub(crate) fn new_external(cmd: &crate::Command) -> Self {
|
||||
let ignore_case = false;
|
||||
Self {
|
||||
occurs: 0,
|
||||
source: None,
|
||||
indices: Vec::new(),
|
||||
type_id: Some(
|
||||
cmd.get_external_subcommand_value_parser()
|
||||
.expect(INTERNAL_ERROR_MSG)
|
||||
.type_id(),
|
||||
),
|
||||
vals: Vec::new(),
|
||||
raw_vals: Vec::new(),
|
||||
ignore_case,
|
||||
|
@ -171,6 +180,10 @@ impl MatchedArg {
|
|||
self.source = Some(source)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn type_id(&self) -> Option<AnyValueId> {
|
||||
self.type_id
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for MatchedArg {
|
||||
|
@ -179,6 +192,7 @@ impl PartialEq for MatchedArg {
|
|||
occurs: self_occurs,
|
||||
source: self_source,
|
||||
indices: self_indices,
|
||||
type_id: self_type_id,
|
||||
vals: _,
|
||||
raw_vals: self_raw_vals,
|
||||
ignore_case: self_ignore_case,
|
||||
|
@ -187,6 +201,7 @@ impl PartialEq for MatchedArg {
|
|||
occurs: other_occurs,
|
||||
source: other_source,
|
||||
indices: other_indices,
|
||||
type_id: other_type_id,
|
||||
vals: _,
|
||||
raw_vals: other_raw_vals,
|
||||
ignore_case: other_ignore_case,
|
||||
|
@ -194,6 +209,7 @@ impl PartialEq for MatchedArg {
|
|||
self_occurs == other_occurs
|
||||
&& self_source == other_source
|
||||
&& self_indices == other_indices
|
||||
&& self_type_id == other_type_id
|
||||
&& self_raw_vals == other_raw_vals
|
||||
&& self_ignore_case == other_ignore_case
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue