Replace ArgStr with os_str_bytes::RawOsStr

This commit is contained in:
dylni 2021-08-29 10:00:30 -04:00
parent ee67039c5e
commit dc65513966
7 changed files with 70 additions and 341 deletions

View file

@ -64,7 +64,7 @@ clap_derive = { path = "./clap_derive", version = "=3.0.0-beta.4", optional = tr
bitflags = "1.2" bitflags = "1.2"
textwrap = { version = "0.14.0", default-features = false, features = [] } textwrap = { version = "0.14.0", default-features = false, features = [] }
indexmap = "1.0" indexmap = "1.0"
os_str_bytes = { version = "3.0", features = ["raw"] } os_str_bytes = "4.1"
vec_map = "0.8" vec_map = "0.8"
strsim = { version = "0.10", optional = true } strsim = { version = "0.10", optional = true }
yaml-rust = { version = "0.4.1", optional = true } yaml-rust = { version = "0.4.1", optional = true }

View file

@ -18,6 +18,7 @@ use std::{
}; };
// Third Party // Third Party
use os_str_bytes::RawOsStr;
#[cfg(feature = "yaml")] #[cfg(feature = "yaml")]
use yaml_rust::Yaml; use yaml_rust::Yaml;
@ -27,7 +28,7 @@ use crate::{
mkeymap::MKeyMap, mkeymap::MKeyMap,
output::{fmt::Colorizer, Help, HelpWriter, Usage}, output::{fmt::Colorizer, Help, HelpWriter, Usage},
parse::{ArgMatcher, ArgMatches, Input, Parser}, parse::{ArgMatcher, ArgMatches, Input, Parser},
util::{safe_exit, termcolor::ColorChoice, ArgStr, Id, Key, USAGE_CODE}, util::{safe_exit, termcolor::ColorChoice, Id, Key, USAGE_CODE},
Result as ClapResult, INTERNAL_ERROR_MSG, Result as ClapResult, INTERNAL_ERROR_MSG,
}; };
@ -2791,7 +2792,7 @@ impl<'help> App<'help> {
} }
/// Find a flag subcommand name by long flag or an alias /// Find a flag subcommand name by long flag or an alias
pub(crate) fn find_long_subcmd(&self, long: &ArgStr) -> Option<&str> { pub(crate) fn find_long_subcmd(&self, long: &RawOsStr) -> Option<&str> {
self.get_subcommands() self.get_subcommands()
.find(|sc| sc.long_flag_aliases_to(long)) .find(|sc| sc.long_flag_aliases_to(long))
.map(|sc| sc.get_name()) .map(|sc| sc.get_name())

View file

@ -1,6 +1,6 @@
use crate::{build::Arg, util::Id, INTERNAL_ERROR_MSG}; use crate::{build::Arg, util::Id, INTERNAL_ERROR_MSG};
use std::{ffi::OsString, iter::Iterator, ops::Index}; use std::{ffi::OsStr, ffi::OsString, iter::Iterator, ops::Index};
#[derive(PartialEq, Eq, Debug, Clone)] #[derive(PartialEq, Eq, Debug, Clone)]
pub(crate) struct Key { pub(crate) struct Key {
@ -49,8 +49,8 @@ impl PartialEq<&str> for KeyType {
} }
} }
impl PartialEq<OsString> for KeyType { impl PartialEq<OsStr> for KeyType {
fn eq(&self, rhs: &OsString) -> bool { fn eq(&self, rhs: &OsStr) -> bool {
match self { match self {
KeyType::Long(l) => l == rhs, KeyType::Long(l) => l == rhs,
_ => false, _ => false,
@ -86,7 +86,7 @@ impl<'help> MKeyMap<'help> {
/// Find the arg have corresponding key in this map, we can search the key /// Find the arg have corresponding key in this map, we can search the key
/// with u64(for positional argument), char(for short flag), &str and /// with u64(for positional argument), char(for short flag), &str and
/// OsString (for long flag) /// OsString (for long flag)
pub(crate) fn get<K>(&self, key: &K) -> Option<&Arg<'help>> pub(crate) fn get<K: ?Sized>(&self, key: &K) -> Option<&Arg<'help>>
where where
KeyType: PartialEq<K>, KeyType: PartialEq<K>,
{ {

View file

@ -4,6 +4,9 @@ use std::{
ffi::{OsStr, OsString}, ffi::{OsStr, OsString},
}; };
// Third Party
use os_str_bytes::RawOsStr;
// Internal // Internal
use crate::{ use crate::{
build::AppSettings as AS, build::AppSettings as AS,
@ -16,7 +19,7 @@ use crate::{
parse::features::suggestions, parse::features::suggestions,
parse::{ArgMatcher, SubCommand}, parse::{ArgMatcher, SubCommand},
parse::{Validator, ValueType}, parse::{Validator, ValueType},
util::{termcolor::ColorChoice, ArgStr, ChildGraph, Id}, util::{termcolor::ColorChoice, ChildGraph, Id},
INTERNAL_ERROR_MSG, INVALID_UTF8, INTERNAL_ERROR_MSG, INVALID_UTF8,
}; };
@ -374,7 +377,7 @@ impl<'help, 'app> Parser<'help, 'app> {
continue; continue;
} }
let arg_os = ArgStr::new(arg_os); let arg_os = RawOsStr::new(arg_os);
debug!( debug!(
"Parser::get_matches_with: Begin parsing '{:?}' ({:?})", "Parser::get_matches_with: Begin parsing '{:?}' ({:?})",
arg_os, arg_os,
@ -423,7 +426,7 @@ impl<'help, 'app> Parser<'help, 'app> {
// pos_counter(which means current value cannot be a // pos_counter(which means current value cannot be a
// positional argument with a value next to it), assume // positional argument with a value next to it), assume
// current value matches the next arg. // current value matches the next arg.
let n = ArgStr::new(n); let n = RawOsStr::new(n);
self.is_new_arg(&n, p) self.is_new_arg(&n, p)
|| self.possible_subcommand(&n, valid_arg_found).is_some() || self.possible_subcommand(&n, valid_arg_found).is_some()
} else { } else {
@ -468,10 +471,10 @@ impl<'help, 'app> Parser<'help, 'app> {
} }
} }
if arg_os.starts_with("--") { if let Some(long_arg) = arg_os.strip_prefix("--") {
let parse_result = self.parse_long_arg( let parse_result = self.parse_long_arg(
matcher, matcher,
&arg_os, long_arg,
&parse_state, &parse_state,
&mut valid_arg_found, &mut valid_arg_found,
trailing_values, trailing_values,
@ -537,7 +540,7 @@ impl<'help, 'app> Parser<'help, 'app> {
unreachable!() unreachable!()
} }
} }
} else if arg_os.starts_with("-") { } else if let Some(short_arg) = arg_os.strip_prefix("-") {
// Arg looks like a short flag, and not a possible number // Arg looks like a short flag, and not a possible number
// Try to parse short args like normal, if AllowLeadingHyphen or // Try to parse short args like normal, if AllowLeadingHyphen or
@ -545,7 +548,7 @@ impl<'help, 'app> Parser<'help, 'app> {
// an error, and instead return Ok(None) // an error, and instead return Ok(None)
let parse_result = self.parse_short_arg( let parse_result = self.parse_short_arg(
matcher, matcher,
&arg_os, short_arg,
&parse_state, &parse_state,
pos_counter, pos_counter,
&mut valid_arg_found, &mut valid_arg_found,
@ -626,7 +629,7 @@ impl<'help, 'app> Parser<'help, 'app> {
// get the option so we can check the settings // get the option so we can check the settings
let parse_result = self.add_val_to_arg( let parse_result = self.add_val_to_arg(
&self.app[id], &self.app[id],
arg_os, &arg_os,
matcher, matcher,
ValueType::CommandLine, ValueType::CommandLine,
true, true,
@ -645,7 +648,7 @@ impl<'help, 'app> Parser<'help, 'app> {
if let Some(p) = self.app.args.get(&pos_counter) { if let Some(p) = self.app.args.get(&pos_counter) {
if p.is_set(ArgSettings::Last) && !trailing_values { if p.is_set(ArgSettings::Last) && !trailing_values {
return Err(ClapError::unknown_argument( return Err(ClapError::unknown_argument(
arg_os.to_string_lossy().to_string(), arg_os.to_str_lossy().into_owned(),
None, None,
Usage::new(self).create_usage_with_title(&[]), Usage::new(self).create_usage_with_title(&[]),
self.app.color(), self.app.color(),
@ -663,7 +666,7 @@ impl<'help, 'app> Parser<'help, 'app> {
let append = self.arg_have_val(matcher, p); let append = self.arg_have_val(matcher, p);
self.add_val_to_arg( self.add_val_to_arg(
p, p,
arg_os, &arg_os,
matcher, matcher,
ValueType::CommandLine, ValueType::CommandLine,
append, append,
@ -768,7 +771,7 @@ impl<'help, 'app> Parser<'help, 'app> {
fn match_arg_error( fn match_arg_error(
&self, &self,
arg_os: &ArgStr, arg_os: &RawOsStr,
valid_arg_found: bool, valid_arg_found: bool,
trailing_values: bool, trailing_values: bool,
) -> ClapError { ) -> ClapError {
@ -777,14 +780,14 @@ impl<'help, 'app> Parser<'help, 'app> {
// If the arg matches a subcommand name, or any of its aliases (if defined) // If the arg matches a subcommand name, or any of its aliases (if defined)
if self.possible_subcommand(arg_os, valid_arg_found).is_some() { if self.possible_subcommand(arg_os, valid_arg_found).is_some() {
return ClapError::unnecessary_double_dash( return ClapError::unnecessary_double_dash(
arg_os.to_string_lossy().to_string(), arg_os.to_str_lossy().into_owned(),
Usage::new(self).create_usage_with_title(&[]), Usage::new(self).create_usage_with_title(&[]),
self.app.color(), self.app.color(),
); );
} }
} }
let candidates = let candidates =
suggestions::did_you_mean(&arg_os.to_string_lossy(), self.app.all_subcommand_names()); suggestions::did_you_mean(&arg_os.to_str_lossy(), self.app.all_subcommand_names());
// If the argument looks like a subcommand. // If the argument looks like a subcommand.
if !candidates.is_empty() { if !candidates.is_empty() {
let candidates: Vec<_> = candidates let candidates: Vec<_> = candidates
@ -792,7 +795,7 @@ impl<'help, 'app> Parser<'help, 'app> {
.map(|candidate| format!("'{}'", candidate)) .map(|candidate| format!("'{}'", candidate))
.collect(); .collect();
return ClapError::invalid_subcommand( return ClapError::invalid_subcommand(
arg_os.to_string_lossy().to_string(), arg_os.to_str_lossy().into_owned(),
candidates.join(" or "), candidates.join(" or "),
self.app self.app
.bin_name .bin_name
@ -806,7 +809,7 @@ impl<'help, 'app> Parser<'help, 'app> {
// If the argument must be a subcommand. // If the argument must be a subcommand.
if !self.app.has_args() || self.is_set(AS::InferSubcommands) && self.app.has_subcommands() { if !self.app.has_args() || self.is_set(AS::InferSubcommands) && self.app.has_subcommands() {
return ClapError::unrecognized_subcommand( return ClapError::unrecognized_subcommand(
arg_os.to_string_lossy().to_string(), arg_os.to_str_lossy().into_owned(),
self.app self.app
.bin_name .bin_name
.as_ref() .as_ref()
@ -816,7 +819,7 @@ impl<'help, 'app> Parser<'help, 'app> {
); );
} }
ClapError::unknown_argument( ClapError::unknown_argument(
arg_os.to_string_lossy().to_string(), arg_os.to_str_lossy().into_owned(),
None, None,
Usage::new(self).create_usage_with_title(&[]), Usage::new(self).create_usage_with_title(&[]),
self.app.color(), self.app.color(),
@ -835,7 +838,7 @@ impl<'help, 'app> Parser<'help, 'app> {
} }
// Checks if the arg matches a subcommand name, or any of its aliases (if defined) // Checks if the arg matches a subcommand name, or any of its aliases (if defined)
fn possible_subcommand(&self, arg_os: &ArgStr, valid_arg_found: bool) -> Option<&str> { fn possible_subcommand(&self, arg_os: &RawOsStr, valid_arg_found: bool) -> Option<&str> {
debug!("Parser::possible_subcommand: arg={:?}", arg_os); debug!("Parser::possible_subcommand: arg={:?}", arg_os);
if !(self.is_set(AS::ArgsNegateSubcommands) && valid_arg_found) { if !(self.is_set(AS::ArgsNegateSubcommands) && valid_arg_found) {
@ -845,7 +848,7 @@ impl<'help, 'app> Parser<'help, 'app> {
let v = self let v = self
.app .app
.all_subcommand_names() .all_subcommand_names()
.filter(|s| arg_os.is_prefix_of(s)) .filter(|s| RawOsStr::from_str(s).starts_with_os(arg_os))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
if v.len() == 1 { if v.len() == 1 {
@ -863,7 +866,7 @@ impl<'help, 'app> Parser<'help, 'app> {
} }
// Checks if the arg matches a long flag subcommand name, or any of its aliases (if defined) // Checks if the arg matches a long flag subcommand name, or any of its aliases (if defined)
fn possible_long_flag_subcommand(&self, arg_os: &ArgStr) -> Option<&str> { fn possible_long_flag_subcommand(&self, arg_os: &RawOsStr) -> Option<&str> {
debug!("Parser::possible_long_flag_subcommand: arg={:?}", arg_os); debug!("Parser::possible_long_flag_subcommand: arg={:?}", arg_os);
if self.is_set(AS::InferSubcommands) { if self.is_set(AS::InferSubcommands) {
let options = self let options = self
@ -871,12 +874,12 @@ impl<'help, 'app> Parser<'help, 'app> {
.get_subcommands() .get_subcommands()
.fold(Vec::new(), |mut options, sc| { .fold(Vec::new(), |mut options, sc| {
if let Some(long) = sc.long_flag { if let Some(long) = sc.long_flag {
if arg_os.is_prefix_of(long) { if RawOsStr::from_str(long).starts_with_os(arg_os) {
options.push(long); options.push(long);
} }
options.extend( options.extend(
sc.get_all_aliases() sc.get_all_aliases()
.filter(|alias| arg_os.is_prefix_of(alias)), .filter(|alias| RawOsStr::from_str(alias).starts_with_os(arg_os)),
) )
} }
options options
@ -885,7 +888,7 @@ impl<'help, 'app> Parser<'help, 'app> {
return Some(options[0]); return Some(options[0]);
} }
for sc in &options { for sc in options {
if sc == arg_os { if sc == arg_os {
return Some(sc); return Some(sc);
} }
@ -930,7 +933,7 @@ impl<'help, 'app> Parser<'help, 'app> {
} }
} else { } else {
return Err(ClapError::unrecognized_subcommand( return Err(ClapError::unrecognized_subcommand(
cmd.to_string_lossy().to_string(), cmd.to_string_lossy().into_owned(),
self.app self.app
.bin_name .bin_name
.as_ref() .as_ref()
@ -968,7 +971,7 @@ impl<'help, 'app> Parser<'help, 'app> {
Err(parser.help_err(self.app.is_set(AS::UseLongFormatForHelpSubcommand))) Err(parser.help_err(self.app.is_set(AS::UseLongFormatForHelpSubcommand)))
} }
fn is_new_arg(&self, next: &ArgStr, current_positional: &Arg) -> bool { fn is_new_arg(&self, next: &RawOsStr, current_positional: &Arg) -> bool {
debug!( debug!(
"Parser::is_new_arg: {:?}:{:?}", "Parser::is_new_arg: {:?}:{:?}",
next, current_positional.name next, current_positional.name
@ -977,7 +980,7 @@ impl<'help, 'app> Parser<'help, 'app> {
if self.is_set(AS::AllowLeadingHyphen) if self.is_set(AS::AllowLeadingHyphen)
|| self.app[&current_positional.id].is_set(ArgSettings::AllowHyphenValues) || self.app[&current_positional.id].is_set(ArgSettings::AllowHyphenValues)
|| (self.is_set(AS::AllowNegativeNumbers) || (self.is_set(AS::AllowNegativeNumbers)
&& next.to_string_lossy().parse::<f64>().is_ok()) && next.to_str_lossy().parse::<f64>().is_ok())
{ {
// If allow hyphen, this isn't a new arg. // If allow hyphen, this isn't a new arg.
debug!("Parser::is_new_arg: Allow hyphen"); debug!("Parser::is_new_arg: Allow hyphen");
@ -990,7 +993,7 @@ impl<'help, 'app> Parser<'help, 'app> {
debug!("Parser::is_new_arg: - found"); debug!("Parser::is_new_arg: - found");
// If this is a short flag, this is a new arg. But a singe '-' by // If this is a short flag, this is a new arg. But a singe '-' by
// itself is a value and typically means "stdin" on unix systems. // itself is a value and typically means "stdin" on unix systems.
next.len() != 1 next.raw_len() != 1
} else { } else {
debug!("Parser::is_new_arg: value"); debug!("Parser::is_new_arg: value");
// Nothing special, this is a value. // Nothing special, this is a value.
@ -1092,7 +1095,7 @@ impl<'help, 'app> Parser<'help, 'app> {
// Retrieves the names of all args the user has supplied thus far, except required ones // Retrieves the names of all args the user has supplied thus far, except required ones
// because those will be listed in self.required // because those will be listed in self.required
fn check_for_help_and_version_str(&self, arg: &ArgStr) -> Option<ParseResult> { fn check_for_help_and_version_str(&self, arg: &RawOsStr) -> Option<ParseResult> {
debug!("Parser::check_for_help_and_version_str"); debug!("Parser::check_for_help_and_version_str");
debug!( debug!(
"Parser::check_for_help_and_version_str: Checking if --{:?} is help or version...", "Parser::check_for_help_and_version_str: Checking if --{:?} is help or version...",
@ -1172,7 +1175,7 @@ impl<'help, 'app> Parser<'help, 'app> {
fn parse_long_arg( fn parse_long_arg(
&mut self, &mut self,
matcher: &mut ArgMatcher, matcher: &mut ArgMatcher,
full_arg: &ArgStr, long_arg: &RawOsStr,
parse_state: &ParseState, parse_state: &ParseState,
valid_arg_found: &mut bool, valid_arg_found: &mut bool,
trailing_values: bool, trailing_values: bool,
@ -1191,12 +1194,11 @@ impl<'help, 'app> Parser<'help, 'app> {
debug!("Parser::parse_long_arg: cur_idx:={}", self.cur_idx.get()); debug!("Parser::parse_long_arg: cur_idx:={}", self.cur_idx.get());
debug!("Parser::parse_long_arg: Does it contain '='..."); debug!("Parser::parse_long_arg: Does it contain '='...");
let long_arg = full_arg.trim_start_n_matches(2, b'-');
if long_arg.is_empty() { if long_arg.is_empty() {
return ParseResult::NoArg; return ParseResult::NoArg;
} }
let (arg, val) = if full_arg.contains_byte(b'=') { let (arg, val) = if let Some(index) = long_arg.find("=") {
let (p0, p1) = long_arg.split_at_byte(b'='); let (p0, p1) = long_arg.split_at(index);
debug!("Yes '{:?}'", p1); debug!("Yes '{:?}'", p1);
(p0, Some(p1)) (p0, Some(p1))
} else { } else {
@ -1204,20 +1206,20 @@ impl<'help, 'app> Parser<'help, 'app> {
(long_arg, None) (long_arg, None)
}; };
let opt = if let Some(opt) = self.app.args.get(&arg.to_os_string()) { let opt = if let Some(opt) = self.app.args.get(&*arg.to_os_str()) {
debug!( debug!(
"Parser::parse_long_arg: Found valid opt or flag '{}'", "Parser::parse_long_arg: Found valid opt or flag '{}'",
opt.to_string() opt.to_string()
); );
Some(opt) Some(opt)
} else if self.is_set(AS::InferLongArgs) { } else if self.is_set(AS::InferLongArgs) {
let arg_str = arg.to_string_lossy(); let arg_str = arg.to_str_lossy();
self.app.args.args().find(|a| { self.app.args.args().find(|a| {
a.long a.long
.map_or(false, |long| long.starts_with(arg_str.as_ref())) .map_or(false, |long| long.starts_with(&*arg_str))
|| a.aliases || a.aliases
.iter() .iter()
.any(|(alias, _)| alias.starts_with(arg_str.as_ref())) .any(|(alias, _)| alias.starts_with(&*arg_str))
}) })
} else { } else {
None None
@ -1231,7 +1233,7 @@ impl<'help, 'app> Parser<'help, 'app> {
"Parser::parse_long_arg: Found an opt with value '{:?}'", "Parser::parse_long_arg: Found an opt with value '{:?}'",
&val &val
); );
self.parse_opt(&val, opt, matcher, trailing_values) self.parse_opt(val, opt, matcher, trailing_values)
} else if let Some(rest) = val { } else if let Some(rest) = val {
debug!( debug!(
"Parser::parse_long_arg: Got invalid literal `{:?}`", "Parser::parse_long_arg: Got invalid literal `{:?}`",
@ -1248,7 +1250,7 @@ impl<'help, 'app> Parser<'help, 'app> {
.collect(); .collect();
ParseResult::UnneededAttachedValue { ParseResult::UnneededAttachedValue {
rest: rest.to_string_lossy().to_string(), rest: rest.to_str_lossy().into_owned(),
used, used,
arg: opt.to_string(), arg: opt.to_string(),
} }
@ -1264,7 +1266,7 @@ impl<'help, 'app> Parser<'help, 'app> {
ParseResult::MaybeHyphenValue ParseResult::MaybeHyphenValue
} else { } else {
ParseResult::NoMatchingArg { ParseResult::NoMatchingArg {
arg: arg.to_string_lossy().to_string(), arg: arg.to_str_lossy().into_owned(),
} }
} }
} }
@ -1272,16 +1274,15 @@ impl<'help, 'app> Parser<'help, 'app> {
fn parse_short_arg( fn parse_short_arg(
&mut self, &mut self,
matcher: &mut ArgMatcher, matcher: &mut ArgMatcher,
full_arg: &ArgStr, short_arg: &RawOsStr,
parse_state: &ParseState, parse_state: &ParseState,
// change this to possible pos_arg when removing the usage of &mut Parser. // change this to possible pos_arg when removing the usage of &mut Parser.
pos_counter: usize, pos_counter: usize,
valid_arg_found: &mut bool, valid_arg_found: &mut bool,
trailing_values: bool, trailing_values: bool,
) -> ParseResult { ) -> ParseResult {
debug!("Parser::parse_short_arg: full_arg={:?}", full_arg); debug!("Parser::parse_short_arg: short_arg={:?}", short_arg);
let arg_os = full_arg.trim_start_matches(b'-'); let arg = short_arg.to_str_lossy();
let arg = arg_os.to_string_lossy();
if (self.is_set(AS::AllowNegativeNumbers) && arg.parse::<f64>().is_ok()) if (self.is_set(AS::AllowNegativeNumbers) && arg.parse::<f64>().is_ok())
|| (self.is_set(AS::AllowLeadingHyphen) || (self.is_set(AS::AllowLeadingHyphen)
@ -1332,24 +1333,12 @@ impl<'help, 'app> Parser<'help, 'app> {
} }
// Check for trailing concatenated value // Check for trailing concatenated value
let i = arg_os.split(c).next().expect(INTERNAL_ERROR_MSG).len() + c.len_utf8(); let val = short_arg.split_once(c).expect(INTERNAL_ERROR_MSG).1;
debug!( debug!(
"Parser::parse_short_arg:iter:{}: i={}, arg_os={:?}", "Parser::parse_short_arg:iter:{}: val={:?} (bytes), val={:?} (ascii), rest={:?}, arg_os={:?}",
c, i, arg_os c, val, val.as_raw_bytes(), arg_os
); );
let val = if i != arg_os.len() { let val = Some(val).filter(|v| !v.is_empty());
// This is always a valid place to split, because the separator is UTF-8.
let val = arg_os.split_at_unchecked(i).1;
debug!(
"Parser::parse_short_arg:iter:{}: val={:?} (bytes), val={:?} (ascii)",
c,
val.as_raw_bytes(),
val
);
Some(val)
} else {
None
};
// Default to "we're expecting a value later". // Default to "we're expecting a value later".
// //
@ -1358,7 +1347,7 @@ impl<'help, 'app> Parser<'help, 'app> {
// //
// e.g. `-xvf`, when RequireEquals && x.min_vals == 0, we don't // e.g. `-xvf`, when RequireEquals && x.min_vals == 0, we don't
// consume the `vf`, even if it's provided as value. // consume the `vf`, even if it's provided as value.
match self.parse_opt(&val, opt, matcher, trailing_values) { match self.parse_opt(val, opt, matcher, trailing_values) {
ParseResult::AttachedValueNotConsumed => continue, ParseResult::AttachedValueNotConsumed => continue,
x => return x, x => return x,
} }
@ -1393,7 +1382,7 @@ impl<'help, 'app> Parser<'help, 'app> {
fn parse_opt( fn parse_opt(
&self, &self,
attached_value: &Option<ArgStr>, attached_value: Option<&RawOsStr>,
opt: &Arg<'help>, opt: &Arg<'help>,
matcher: &mut ArgMatcher, matcher: &mut ArgMatcher,
trailing_values: bool, trailing_values: bool,
@ -1435,8 +1424,8 @@ impl<'help, 'app> Parser<'help, 'app> {
} }
} }
} else if let Some(fv) = attached_value { } else if let Some(fv) = attached_value {
let v = fv.trim_start_n_matches(1, b'='); let v = fv.strip_prefix("=").unwrap_or(fv);
debug!("Found - {:?}, len: {}", v, v.len()); debug!("Found - {:?}, len: {}", v, v.raw_len());
debug!( debug!(
"Parser::parse_opt: {:?} contains '='...{:?}", "Parser::parse_opt: {:?} contains '='...{:?}",
fv, fv,
@ -1466,7 +1455,7 @@ impl<'help, 'app> Parser<'help, 'app> {
fn add_val_to_arg( fn add_val_to_arg(
&self, &self,
arg: &Arg<'help>, arg: &Arg<'help>,
val: ArgStr, val: &RawOsStr,
matcher: &mut ArgMatcher, matcher: &mut ArgMatcher,
ty: ValueType, ty: ValueType,
append: bool, append: bool,
@ -1495,7 +1484,7 @@ impl<'help, 'app> Parser<'help, 'app> {
}; };
self.add_multiple_vals_to_arg( self.add_multiple_vals_to_arg(
arg, arg,
vals.into_iter().map(|x| x.into_os_string()), vals.into_iter().map(|x| x.to_os_str().into_owned()),
matcher, matcher,
ty, ty,
append, append,
@ -1503,7 +1492,7 @@ impl<'help, 'app> Parser<'help, 'app> {
// If there was a delimiter used or we must use the delimiter to // If there was a delimiter used or we must use the delimiter to
// separate the values or no more vals is needed, we're not // separate the values or no more vals is needed, we're not
// looking for more values. // looking for more values.
return if val.contains_char(delim) return if val.contains(delim)
|| arg.is_set(ArgSettings::RequireDelimiter) || arg.is_set(ArgSettings::RequireDelimiter)
|| !matcher.needs_more_vals(arg) || !matcher.needs_more_vals(arg)
{ {
@ -1518,7 +1507,7 @@ impl<'help, 'app> Parser<'help, 'app> {
return ParseResult::ValuesDone; return ParseResult::ValuesDone;
} }
} }
self.add_single_val_to_arg(arg, val.to_os_string(), matcher, ty, append); self.add_single_val_to_arg(arg, val.to_os_str().into_owned(), matcher, ty, append);
if matcher.needs_more_vals(arg) { if matcher.needs_more_vals(arg) {
ParseResult::Opt(arg.id.clone()) ParseResult::Opt(arg.id.clone())
} else { } else {
@ -1686,7 +1675,7 @@ impl<'help, 'app> Parser<'help, 'app> {
if let Some(default) = default { if let Some(default) = default {
self.add_val_to_arg( self.add_val_to_arg(
arg, arg,
ArgStr::new(default), &RawOsStr::new(default),
matcher, matcher,
ty, ty,
false, false,
@ -1782,7 +1771,7 @@ impl<'help, 'app> Parser<'help, 'app> {
debug!("Parser::add_env: Checking arg `{}`", a); debug!("Parser::add_env: Checking arg `{}`", a);
if let Some((_, Some(ref val))) = a.env { if let Some((_, Some(ref val))) = a.env {
let val = ArgStr::new(val); let val = RawOsStr::new(val);
if a.is_set(ArgSettings::TakesValue) { if a.is_set(ArgSettings::TakesValue) {
debug!( debug!(
@ -1791,7 +1780,7 @@ impl<'help, 'app> Parser<'help, 'app> {
); );
self.add_val_to_arg( self.add_val_to_arg(
a, a,
val, &val,
matcher, matcher,
ValueType::EnvVariable, ValueType::EnvVariable,
false, false,
@ -1813,7 +1802,7 @@ impl<'help, 'app> Parser<'help, 'app> {
} }
debug!("Parser::add_env: Found a flag with value `{:?}`", val); debug!("Parser::add_env: Found a flag with value `{:?}`", val);
let predicate = str_to_bool(val.to_string_lossy()); let predicate = str_to_bool(val.to_str_lossy());
debug!("Parser::add_env: Found boolean literal `{}`", predicate); debug!("Parser::add_env: Found boolean literal `{}`", predicate);
if predicate { if predicate {
matcher.add_index_to(&a.id, self.cur_idx.get(), ValueType::EnvVariable); matcher.add_index_to(&a.id, self.cur_idx.get(), ValueType::EnvVariable);

View file

@ -122,7 +122,7 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> {
.cloned() .cloned()
.collect(); .collect();
return Err(Error::invalid_value( return Err(Error::invalid_value(
val_str.to_string(), val_str.into_owned(),
&arg.possible_vals, &arg.possible_vals,
arg, arg,
Usage::new(self.p).create_usage_with_title(&used), Usage::new(self.p).create_usage_with_title(&used),
@ -149,7 +149,7 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> {
debug!("error"); debug!("error");
return Err(Error::value_validation( return Err(Error::value_validation(
arg.to_string(), arg.to_string(),
val.to_string_lossy().to_string(), val.to_string_lossy().into_owned(),
e, e,
self.p.app.color(), self.p.app.color(),
)); ));

View file

@ -1,260 +0,0 @@
use std::{
borrow::Cow,
ffi::{OsStr, OsString},
fmt::{self, Debug},
str,
};
use os_str_bytes::{raw, OsStrBytes};
#[derive(PartialEq, Eq)]
pub(crate) struct ArgStr<'a>(Cow<'a, [u8]>);
impl<'a> ArgStr<'a> {
pub(crate) fn new(s: &'a OsStr) -> Self {
Self(s.to_raw_bytes())
}
pub(crate) fn starts_with(&self, s: &str) -> bool {
self.0.starts_with(s.as_bytes())
}
pub(crate) fn is_prefix_of(&self, s: &str) -> bool {
raw::starts_with(s, &self.0)
}
fn to_borrowed(&'a self) -> Self {
Self(Cow::Borrowed(&self.0))
}
pub(crate) fn contains_byte(&self, byte: u8) -> bool {
assert!(byte.is_ascii());
self.0.contains(&byte)
}
pub(crate) fn contains_char(&self, ch: char) -> bool {
let mut bytes = [0; 4];
let bytes = ch.encode_utf8(&mut bytes).as_bytes();
for i in 0..self.0.len().saturating_sub(bytes.len() - 1) {
if self.0[i..].starts_with(bytes) {
return true;
}
}
false
}
pub(crate) fn split_at_byte(&self, byte: u8) -> (ArgStr, ArgStr) {
assert!(byte.is_ascii());
if let Some(i) = self.0.iter().position(|&x| x == byte) {
self.split_at_unchecked(i)
} else {
(self.to_borrowed(), Self(Cow::Borrowed(&[])))
}
}
pub(crate) fn trim_start_matches(&'a self, byte: u8) -> ArgStr {
assert!(byte.is_ascii());
if let Some(i) = self.0.iter().position(|x| x != &byte) {
Self(Cow::Borrowed(&self.0[i..]))
} else {
Self(Cow::Borrowed(&[]))
}
}
// Like `trim_start_matches`, but trims no more than `n` matches
pub(crate) fn trim_start_n_matches(&self, n: usize, ch: u8) -> ArgStr {
assert!(ch.is_ascii());
let i = self.0.iter().take(n).take_while(|c| **c == ch).count();
self.split_at_unchecked(i).1
}
pub(crate) fn split_at_unchecked(&'a self, i: usize) -> (ArgStr, ArgStr) {
(
Self(Cow::Borrowed(&self.0[..i])),
Self(Cow::Borrowed(&self.0[i..])),
)
}
pub(crate) fn split(&self, ch: char) -> ArgSplit<'_> {
let mut sep = [0; 4];
ArgSplit {
sep_len: ch.encode_utf8(&mut sep).as_bytes().len(),
sep,
val: &self.0,
pos: 0,
}
}
#[allow(dead_code)]
pub(crate) fn as_raw_bytes(&self) -> &[u8] {
&self.0
}
pub(crate) fn len(&self) -> usize {
self.0.len()
}
#[allow(dead_code)]
pub(crate) fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub(crate) fn to_str(&self) -> Option<&str> {
str::from_utf8(&self.0).ok()
}
pub(crate) fn to_string_lossy(&self) -> Cow<'_, str> {
String::from_utf8_lossy(&self.0)
}
pub(crate) fn to_os_string(&self) -> OsString {
self.to_borrowed().into_os_string()
}
pub(crate) fn into_os_string(self) -> OsString {
OsStr::from_raw_bytes(self.0).unwrap().into_owned()
}
}
impl<'a> Debug for ArgStr<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self.to_string_lossy())
}
}
impl<'a> PartialEq<str> for ArgStr<'a> {
fn eq(&self, other: &str) -> bool {
self.0 == other.as_bytes()
}
}
impl<'a> PartialEq<&str> for ArgStr<'a> {
fn eq(&self, other: &&str) -> bool {
self.eq(*other)
}
}
impl<'a> PartialEq<ArgStr<'a>> for str {
fn eq(&self, other: &ArgStr<'a>) -> bool {
other.eq(self)
}
}
impl<'a> PartialEq<ArgStr<'a>> for &str {
fn eq(&self, other: &ArgStr<'a>) -> bool {
other.eq(self)
}
}
#[derive(Clone, Debug)]
pub(crate) struct ArgSplit<'a> {
sep: [u8; 4],
sep_len: usize,
val: &'a [u8],
pos: usize,
}
impl<'a> Iterator for ArgSplit<'a> {
type Item = ArgStr<'a>;
fn next(&mut self) -> Option<ArgStr<'a>> {
debug!("ArgSplit::next: self={:?}", self);
if self.pos == self.val.len() {
return None;
}
let start = self.pos;
while self.pos < self.val.len() {
if self.val[self.pos..].starts_with(&self.sep[..self.sep_len]) {
let arg = ArgStr(Cow::Borrowed(&self.val[start..self.pos]));
self.pos += self.sep_len;
return Some(arg);
}
self.pos += 1;
}
Some(ArgStr(Cow::Borrowed(&self.val[start..])))
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
#[rustfmt::skip]
fn test_trim_start_matches() {
let raw = OsString::from("hello? world");
let a = ArgStr::new(&raw);
let trimmed = a.trim_start_matches(b'-');
assert_eq!(trimmed, a);
let raw = OsString::from("------------hello? world");
let a = ArgStr::new(&raw);
let trimmed = a.trim_start_matches(b'-');
assert_eq!(trimmed, ArgStr::new(&OsString::from("hello? world")));
let raw = OsString::from("------------hel-lo? -world");
let a = ArgStr::new(&raw);
let trimmed = a.trim_start_matches(b'-');
assert_eq!(trimmed, ArgStr::new(&OsString::from("hel-lo? -world")));
let raw = OsString::from("hel-lo? -world");
let a = ArgStr::new(&raw);
let trimmed = a.trim_start_matches(b'-');
assert_eq!(trimmed, ArgStr::new(&OsString::from("hel-lo? -world")));
let raw = OsString::from("");
let a = ArgStr::new(&raw);
let trimmed = a.trim_start_matches(b'-');
assert_eq!(trimmed, ArgStr::new(&OsString::from("")));
}
#[test]
#[rustfmt::skip]
fn test_trim_start_n_matches() {
let raw = OsString::from("hello? world");
let a = ArgStr::new(&raw);
let trimmed = a.trim_start_n_matches(2, b'-');
assert_eq!(trimmed, a);
let raw = OsString::from("------------hello? world");
let a = ArgStr::new(&raw);
let trimmed = a.trim_start_n_matches(2, b'-');
assert_eq!(trimmed, ArgStr::new(&OsString::from("----------hello? world")));
let raw = OsString::from("------------hello? world");
let a = ArgStr::new(&raw);
let trimmed = a.trim_start_n_matches(1000, b'-');
assert_eq!(trimmed, ArgStr::new(&OsString::from("hello? world")));
let raw = OsString::from("------------hel-lo? -world");
let a = ArgStr::new(&raw);
let trimmed = a.trim_start_n_matches(2, b'-');
assert_eq!(trimmed, ArgStr::new(&OsString::from("----------hel-lo? -world")));
let raw = OsString::from("-hel-lo? -world");
let a = ArgStr::new(&raw);
let trimmed = a.trim_start_n_matches(5, b'-');
assert_eq!(trimmed, ArgStr::new(&OsString::from("hel-lo? -world")));
let raw = OsString::from("hel-lo? -world");
let a = ArgStr::new(&raw);
let trimmed = a.trim_start_n_matches(10, b'-');
assert_eq!(trimmed, ArgStr::new(&OsString::from("hel-lo? -world")));
let raw = OsString::from("");
let a = ArgStr::new(&raw);
let trimmed = a.trim_start_n_matches(10, b'-');
assert_eq!(trimmed, ArgStr::new(&OsString::from("")));
let raw = OsString::from("");
let a = ArgStr::new(&raw);
let trimmed = a.trim_start_n_matches(0, b'-');
assert_eq!(trimmed, ArgStr::new(&OsString::from("")));
}
}

View file

@ -1,6 +1,5 @@
#![allow(clippy::single_component_path_imports)] #![allow(clippy::single_component_path_imports)]
mod argstr;
mod fnv; mod fnv;
mod graph; mod graph;
mod id; mod id;
@ -11,7 +10,7 @@ pub use self::fnv::Key;
#[cfg(feature = "env")] #[cfg(feature = "env")]
pub(crate) use self::str_to_bool::str_to_bool; pub(crate) use self::str_to_bool::str_to_bool;
pub(crate) use self::{argstr::ArgStr, graph::ChildGraph, id::Id}; pub(crate) use self::{graph::ChildGraph, id::Id};
pub(crate) use vec_map::VecMap; pub(crate) use vec_map::VecMap;
#[cfg(feature = "color")] #[cfg(feature = "color")]