mirror of
https://github.com/clap-rs/clap
synced 2024-11-10 06:44:16 +00:00
imp(src/util/osstringext.rs): Remove the last unsafe block
This commit is contained in:
parent
0293fd7c4a
commit
c2246cf03f
6 changed files with 187 additions and 165 deletions
|
@ -63,6 +63,7 @@ bitflags = "1.2"
|
|||
unicode-width = "0.1"
|
||||
textwrap = "0.11"
|
||||
indexmap = "1.0"
|
||||
os_str_bytes = "2.1"
|
||||
strsim = { version = "0.10", optional = true }
|
||||
yaml-rust = { version = "0.4.1", optional = true }
|
||||
atty = { version = "0.2", optional = true }
|
||||
|
|
|
@ -10,8 +10,6 @@ use std::cmp::{Ord, Ordering};
|
|||
use std::env;
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
#[cfg(not(any(target_os = "windows", target_arch = "wasm32")))]
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use std::rc::Rc;
|
||||
use std::str;
|
||||
|
||||
|
@ -20,8 +18,6 @@ use crate::util::VecMap;
|
|||
|
||||
// Internal
|
||||
use crate::build::{arg::settings::ArgFlags, usage_parser::UsageParser};
|
||||
#[cfg(any(target_os = "windows", target_arch = "wasm32"))]
|
||||
use crate::util::OsStrExt3;
|
||||
use crate::util::{Id, Key};
|
||||
use crate::INTERNAL_ERROR_MSG;
|
||||
|
||||
|
@ -2346,7 +2342,7 @@ impl<'help> Arg<'help> {
|
|||
/// [`Arg::default_value_if`]: ./struct.Arg.html#method.default_value_if
|
||||
#[inline]
|
||||
pub fn default_value(self, val: &'help str) -> Self {
|
||||
self.default_values_os(&[OsStr::from_bytes(val.as_bytes())])
|
||||
self.default_values_os(&[OsStr::new(val)])
|
||||
}
|
||||
|
||||
/// Provides a default value in the exact same manner as [`Arg::default_value`]
|
||||
|
@ -2362,10 +2358,7 @@ impl<'help> Arg<'help> {
|
|||
/// [`Arg::default_value`]: ./struct.Arg.html#method.default_value
|
||||
#[inline]
|
||||
pub fn default_values(self, vals: &[&'help str]) -> Self {
|
||||
let vals_vec: Vec<_> = vals
|
||||
.iter()
|
||||
.map(|val| OsStr::from_bytes(val.as_bytes()))
|
||||
.collect();
|
||||
let vals_vec: Vec<_> = vals.iter().map(|val| OsStr::new(*val)).collect();
|
||||
self.default_values_os(&vals_vec[..])
|
||||
}
|
||||
|
||||
|
@ -2482,11 +2475,7 @@ impl<'help> Arg<'help> {
|
|||
val: Option<&'help str>,
|
||||
default: &'help str,
|
||||
) -> Self {
|
||||
self.default_value_if_os(
|
||||
arg_id,
|
||||
val.map(str::as_bytes).map(OsStr::from_bytes),
|
||||
OsStr::from_bytes(default.as_bytes()),
|
||||
)
|
||||
self.default_value_if_os(arg_id, val.map(OsStr::new), OsStr::new(default))
|
||||
}
|
||||
|
||||
/// Provides a conditional default value in the exact same manner as [`Arg::default_value_if`]
|
||||
|
@ -2601,11 +2590,7 @@ impl<'help> Arg<'help> {
|
|||
ifs: &[(T, Option<&'help str>, &'help str)],
|
||||
) -> Self {
|
||||
for (arg, val, default) in ifs {
|
||||
self = self.default_value_if_os(
|
||||
arg,
|
||||
val.map(str::as_bytes).map(OsStr::from_bytes),
|
||||
OsStr::from_bytes(default.as_bytes()),
|
||||
);
|
||||
self = self.default_value_if_os(arg, val.map(OsStr::new), OsStr::new(*default));
|
||||
}
|
||||
self
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Std
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::OsStr;
|
||||
use std::ffi::OsString;
|
||||
use std::mem;
|
||||
use std::ops::Deref;
|
||||
|
||||
|
@ -136,13 +136,13 @@ impl ArgMatcher {
|
|||
self.insert(arg);
|
||||
}
|
||||
|
||||
pub(crate) fn add_val_to(&mut self, arg: &Id, val: &OsStr) {
|
||||
pub(crate) fn add_val_to(&mut self, arg: &Id, val: OsString) {
|
||||
let ma = self.entry(arg).or_insert(MatchedArg {
|
||||
occurs: 0, // @TODO @question Shouldn't this be 1 if we're already adding a value to this arg?
|
||||
indices: Vec::with_capacity(1),
|
||||
vals: Vec::with_capacity(1),
|
||||
});
|
||||
ma.vals.push(val.to_owned());
|
||||
ma.vals.push(val);
|
||||
}
|
||||
|
||||
pub(crate) fn add_index_to(&mut self, arg: &Id, idx: usize) {
|
||||
|
|
|
@ -3,8 +3,6 @@ use std::cell::Cell;
|
|||
use std::ffi::{OsStr, OsString};
|
||||
use std::io::Write;
|
||||
use std::mem;
|
||||
#[cfg(not(any(target_os = "windows", target_arch = "wasm32")))]
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
|
||||
// Internal
|
||||
use crate::build::app::Propagation;
|
||||
|
@ -18,9 +16,7 @@ use crate::parse::errors::Result as ClapResult;
|
|||
use crate::parse::features::suggestions;
|
||||
use crate::parse::Validator;
|
||||
use crate::parse::{ArgMatcher, SubCommand};
|
||||
#[cfg(any(target_os = "windows", target_arch = "wasm32"))]
|
||||
use crate::util::OsStrExt3;
|
||||
use crate::util::{termcolor::ColorChoice, ChildGraph, Id, OsStrExt2};
|
||||
use crate::util::{termcolor::ColorChoice, ArgStr, ChildGraph, Id};
|
||||
use crate::INTERNAL_ERROR_MSG;
|
||||
use crate::INVALID_UTF8;
|
||||
|
||||
|
@ -431,16 +427,15 @@ where
|
|||
while let Some((arg_os, next_arg)) = it.next(replace) {
|
||||
replace = None;
|
||||
|
||||
let arg_os = ArgStr::new(arg_os);
|
||||
debug!(
|
||||
"Parser::get_matches_with: Begin parsing '{:?}' ({:?})",
|
||||
arg_os,
|
||||
&*arg_os.as_bytes()
|
||||
arg_os.as_raw_bytes()
|
||||
);
|
||||
|
||||
for (key, val) in &self.app.replacers {
|
||||
let key_bytes = OsStr::new(key).as_bytes();
|
||||
|
||||
if key_bytes == arg_os.as_bytes() {
|
||||
if *key == arg_os {
|
||||
debug!(
|
||||
"Parser::get_matches_with: found replacer: {:?}, target: {:?}",
|
||||
key, val
|
||||
|
@ -456,13 +451,9 @@ where
|
|||
self.unset(AS::ValidNegNumFound);
|
||||
|
||||
// Is this a new argument, or values from a previous option?
|
||||
let starts_new_arg = self.is_new_arg(arg_os, needs_val_of.clone());
|
||||
let starts_new_arg = self.is_new_arg(&arg_os, needs_val_of.clone());
|
||||
|
||||
if !self.is_set(AS::TrailingValues)
|
||||
&& arg_os.starts_with(b"--")
|
||||
&& arg_os.len() == 2
|
||||
&& starts_new_arg
|
||||
{
|
||||
if !self.is_set(AS::TrailingValues) && arg_os == "--" && starts_new_arg {
|
||||
debug!("Parser::get_matches_with: setting TrailingVals=true");
|
||||
self.set(AS::TrailingValues);
|
||||
continue;
|
||||
|
@ -475,7 +466,7 @@ where
|
|||
ParseResult::Opt(_) | ParseResult::Pos(_)
|
||||
if !self.is_set(AS::SubcommandPrecedenceOverArg) => {}
|
||||
_ => {
|
||||
let sc_name = self.possible_subcommand(arg_os);
|
||||
let sc_name = self.possible_subcommand(&arg_os);
|
||||
debug!(
|
||||
"Parser::get_matches_with: possible_sc={:?}, sc={:?}",
|
||||
sc_name.is_some(),
|
||||
|
@ -496,8 +487,8 @@ where
|
|||
}
|
||||
|
||||
if starts_new_arg {
|
||||
if arg_os.starts_with(b"--") {
|
||||
needs_val_of = self.parse_long_arg(matcher, arg_os)?;
|
||||
if arg_os.starts_with("--") {
|
||||
needs_val_of = self.parse_long_arg(matcher, &arg_os)?;
|
||||
debug!(
|
||||
"Parser::get_matches_with: After parse_long_arg {:?}",
|
||||
needs_val_of
|
||||
|
@ -508,11 +499,11 @@ where
|
|||
}
|
||||
_ => (),
|
||||
}
|
||||
} else if arg_os.starts_with(b"-") && arg_os.len() != 1 {
|
||||
} else if arg_os.starts_with("-") && arg_os.len() != 1 {
|
||||
// Try to parse short args like normal, if AllowLeadingHyphen or
|
||||
// AllowNegativeNumbers is set, parse_short_arg will *not* throw
|
||||
// an error, and instead return Ok(None)
|
||||
needs_val_of = self.parse_short_arg(matcher, arg_os)?;
|
||||
needs_val_of = self.parse_short_arg(matcher, &arg_os)?;
|
||||
// If it's None, we then check if one of those two AppSettings was set
|
||||
debug!(
|
||||
"Parser::get_matches_with: After parse_short_arg {:?}",
|
||||
|
@ -520,11 +511,12 @@ where
|
|||
);
|
||||
match needs_val_of {
|
||||
ParseResult::MaybeNegNum => {
|
||||
if !(arg_os.to_string_lossy().parse::<i64>().is_ok()
|
||||
|| arg_os.to_string_lossy().parse::<f64>().is_ok())
|
||||
let lossy_arg = arg_os.to_string_lossy();
|
||||
if !(lossy_arg.parse::<i64>().is_ok()
|
||||
|| lossy_arg.parse::<f64>().is_ok())
|
||||
{
|
||||
return Err(ClapError::unknown_argument(
|
||||
&*arg_os.to_string_lossy(),
|
||||
lossy_arg,
|
||||
None,
|
||||
&*Usage::new(self).create_usage_with_title(&[]),
|
||||
self.app.color(),
|
||||
|
@ -541,7 +533,7 @@ where
|
|||
// Check to see if parsing a value from a previous arg
|
||||
let arg = self.app.find(&name).expect(INTERNAL_ERROR_MSG);
|
||||
// get the option so we can check the settings
|
||||
needs_val_of = self.add_val_to_arg(arg, arg_os, matcher)?;
|
||||
needs_val_of = self.add_val_to_arg(arg, &arg_os, matcher)?;
|
||||
// get the next value from the iterator
|
||||
continue;
|
||||
}
|
||||
|
@ -610,9 +602,10 @@ where
|
|||
ParseResult::ValuesDone
|
||||
};
|
||||
|
||||
let sc_match = { self.possible_subcommand(n).is_some() };
|
||||
let n = ArgStr::new(n);
|
||||
let sc_match = { self.possible_subcommand(&n).is_some() };
|
||||
|
||||
if self.is_new_arg(n, needs_val_of.clone())
|
||||
if self.is_new_arg(&n, needs_val_of.clone())
|
||||
|| sc_match
|
||||
|| !suggestions::did_you_mean(&n.to_string_lossy(), sc_names!(self.app))
|
||||
.is_empty()
|
||||
|
@ -679,7 +672,7 @@ where
|
|||
}
|
||||
|
||||
self.seen.push(p.id.clone());
|
||||
let _ = self.add_val_to_arg(p, arg_os, matcher)?;
|
||||
let _ = self.add_val_to_arg(p, &arg_os, matcher)?;
|
||||
|
||||
matcher.inc_occurrence_of(&p.id);
|
||||
for grp in groups_for_arg!(self.app, p.id) {
|
||||
|
@ -717,7 +710,7 @@ where
|
|||
self.app.color(),
|
||||
)?);
|
||||
}
|
||||
sc_m.add_val_to(&Id::empty_hash(), &v);
|
||||
sc_m.add_val_to(&Id::empty_hash(), v.to_os_string());
|
||||
}
|
||||
|
||||
matcher.subcommand(SubCommand {
|
||||
|
@ -729,7 +722,7 @@ where
|
|||
break;
|
||||
} else if !((self.is_set(AS::AllowLeadingHyphen)
|
||||
|| self.is_set(AS::AllowNegativeNumbers))
|
||||
&& arg_os.starts_with(b"-"))
|
||||
&& arg_os.starts_with("-"))
|
||||
&& !self.is_set(AS::InferSubcommands)
|
||||
{
|
||||
return Err(ClapError::unknown_argument(
|
||||
|
@ -810,13 +803,10 @@ where
|
|||
}
|
||||
|
||||
// Checks if the arg matches a subcommand name, or any of it's aliases (if defined)
|
||||
fn possible_subcommand(&self, arg_os: &OsStr) -> Option<&str> {
|
||||
fn possible_subcommand(&self, arg_os: &ArgStr<'_>) -> Option<&str> {
|
||||
debug!("Parser::possible_subcommand: arg={:?}", arg_os);
|
||||
fn starts(h: &str, n: &OsStr) -> bool {
|
||||
let n_bytes = n.as_bytes();
|
||||
let h_bytes = OsStr::new(h).as_bytes();
|
||||
|
||||
h_bytes.starts_with(n_bytes)
|
||||
fn starts(h: &str, n: &ArgStr<'_>) -> bool {
|
||||
h.is_char_boundary(n.len()) && h.as_bytes().starts_with(n.as_raw_bytes())
|
||||
}
|
||||
|
||||
if self.is_set(AS::ArgsNegateSubcommands) && self.is_set(AS::ValidArgFound) {
|
||||
|
@ -837,7 +827,7 @@ where
|
|||
}
|
||||
|
||||
for sc in &v {
|
||||
if OsStr::new(sc) == arg_os {
|
||||
if sc == arg_os {
|
||||
return Some(sc);
|
||||
}
|
||||
}
|
||||
|
@ -916,7 +906,7 @@ where
|
|||
Err(parser.help_err(false))
|
||||
}
|
||||
|
||||
fn is_new_arg(&mut self, arg_os: &OsStr, needs_val_of: ParseResult) -> bool {
|
||||
fn is_new_arg(&mut self, arg_os: &ArgStr<'_>, needs_val_of: ParseResult) -> bool {
|
||||
debug!("Parser::is_new_arg: {:?}:{:?}", arg_os, needs_val_of);
|
||||
|
||||
let app_wide_settings = if self.is_set(AS::AllowLeadingHyphen) {
|
||||
|
@ -950,7 +940,7 @@ where
|
|||
debug!("Parser::is_new_arg: arg_allows_tac={:?}", arg_allows_tac);
|
||||
|
||||
// Is this a new argument, or values from a previous option?
|
||||
let mut ret = if arg_os.starts_with(b"--") {
|
||||
let mut ret = if arg_os.starts_with("--") {
|
||||
debug!("Parser::is_new_arg: -- found");
|
||||
|
||||
if arg_os.len() == 2 && !arg_allows_tac {
|
||||
|
@ -960,7 +950,7 @@ where
|
|||
}
|
||||
|
||||
true
|
||||
} else if arg_os.starts_with(b"-") {
|
||||
} else if arg_os.starts_with("-") {
|
||||
debug!("Parser::is_new_arg: - found");
|
||||
// a singe '-' by itself is a value and typically means "stdin" on unix systems
|
||||
arg_os.len() != 1
|
||||
|
@ -1043,11 +1033,11 @@ 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: &ArgStr<'_>) -> ClapResult<()> {
|
||||
debug!("Parser::check_for_help_and_version_str");
|
||||
debug!(
|
||||
"Parser::check_for_help_and_version_str: Checking if --{} is help or version...",
|
||||
arg.to_str().unwrap()
|
||||
"Parser::check_for_help_and_version_str: Checking if --{:?} is help or version...",
|
||||
arg
|
||||
);
|
||||
|
||||
// Needs to use app.settings.is_set instead of just is_set() because is_set() checks
|
||||
|
@ -1113,7 +1103,7 @@ where
|
|||
fn parse_long_arg(
|
||||
&mut self,
|
||||
matcher: &mut ArgMatcher,
|
||||
full_arg: &OsStr,
|
||||
full_arg: &ArgStr<'_>,
|
||||
) -> ClapResult<ParseResult> {
|
||||
// maybe here lifetime should be 'a
|
||||
debug!("Parser::parse_long_arg");
|
||||
|
@ -1123,8 +1113,10 @@ where
|
|||
|
||||
let mut val = None;
|
||||
debug!("Parser::parse_long_arg: Does it contain '='...");
|
||||
let matches;
|
||||
let arg = if full_arg.contains_byte(b'=') {
|
||||
let (p0, p1) = full_arg.trim_start_matches(b'-').split_at_byte(b'=');
|
||||
matches = full_arg.trim_start_matches(b'-');
|
||||
let (p0, p1) = matches.split_at_byte(b'=');
|
||||
debug!("Yes '{:?}'", p1);
|
||||
val = Some(p1);
|
||||
p0
|
||||
|
@ -1132,7 +1124,7 @@ where
|
|||
debug!("No");
|
||||
full_arg.trim_start_matches(b'-')
|
||||
};
|
||||
if let Some(opt) = self.app.args.get(&KeyType::Long(arg.into())) {
|
||||
if let Some(opt) = self.app.args.get(&KeyType::Long(arg.to_os_string())) {
|
||||
debug!(
|
||||
"Parser::parse_long_arg: Found valid opt or flag '{}'",
|
||||
opt.to_string()
|
||||
|
@ -1142,9 +1134,9 @@ where
|
|||
self.seen.push(opt.id.clone());
|
||||
|
||||
if opt.is_set(ArgSettings::TakesValue) {
|
||||
return Ok(self.parse_opt(val, opt, val.is_some(), matcher)?);
|
||||
return Ok(self.parse_opt(&val, opt, val.is_some(), matcher)?);
|
||||
}
|
||||
self.check_for_help_and_version_str(arg)?;
|
||||
self.check_for_help_and_version_str(&arg)?;
|
||||
self.parse_flag(opt, matcher)?;
|
||||
|
||||
return Ok(ParseResult::Flag);
|
||||
|
@ -1162,7 +1154,7 @@ where
|
|||
fn parse_short_arg(
|
||||
&mut self,
|
||||
matcher: &mut ArgMatcher,
|
||||
full_arg: &OsStr,
|
||||
full_arg: &ArgStr<'_>,
|
||||
) -> ClapResult<ParseResult> {
|
||||
debug!("Parser::parse_short_arg: full_arg={:?}", full_arg);
|
||||
let arg_os = full_arg.trim_start_matches(b'-');
|
||||
|
@ -1217,21 +1209,23 @@ where
|
|||
p[0].as_bytes(),
|
||||
p[1].as_bytes()
|
||||
);
|
||||
// This is always a valid place to split, because the separator
|
||||
// is UTF-8.
|
||||
let i = p[0].as_bytes().len() + c.len_utf8();
|
||||
let val = if !p[1].is_empty() {
|
||||
debug!(
|
||||
"Parser::parse_short_arg:iter:{}: val={:?} (bytes), val={:?} (ascii)",
|
||||
c,
|
||||
arg_os.split_at(i).1.as_bytes(),
|
||||
arg_os.split_at(i).1
|
||||
arg_os.split_at_unchecked(i).1.as_raw_bytes(),
|
||||
arg_os.split_at_unchecked(i).1
|
||||
);
|
||||
Some(arg_os.split_at(i).1)
|
||||
Some(arg_os.split_at_unchecked(i).1)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Default to "we're expecting a value later"
|
||||
return self.parse_opt(val, opt, false, matcher);
|
||||
return self.parse_opt(&val, opt, false, matcher);
|
||||
} else {
|
||||
let arg = format!("-{}", c);
|
||||
|
||||
|
@ -1248,7 +1242,7 @@ where
|
|||
|
||||
fn parse_opt(
|
||||
&self,
|
||||
val: Option<&OsStr>,
|
||||
val: &Option<ArgStr<'_>>,
|
||||
opt: &Arg<'b>,
|
||||
had_eq: bool,
|
||||
matcher: &mut ArgMatcher,
|
||||
|
@ -1263,7 +1257,7 @@ where
|
|||
|
||||
debug!("Parser::parse_opt; Checking for val...");
|
||||
if let Some(fv) = val {
|
||||
has_eq = fv.starts_with(&[b'=']) || had_eq;
|
||||
has_eq = fv.starts_with("=") || had_eq;
|
||||
let v = fv.trim_start_n_matches(1, b'=');
|
||||
if !empty_vals && (v.is_empty() || (needs_eq && !has_eq)) {
|
||||
debug!("Found Empty - Error");
|
||||
|
@ -1277,9 +1271,9 @@ where
|
|||
debug!(
|
||||
"Parser::parse_opt: {:?} contains '='...{:?}",
|
||||
fv,
|
||||
fv.starts_with(&[b'='])
|
||||
fv.starts_with("=")
|
||||
);
|
||||
self.add_val_to_arg(opt, v, matcher)?;
|
||||
self.add_val_to_arg(opt, &v, matcher)?;
|
||||
} else if needs_eq && !(empty_vals || min_vals_zero) {
|
||||
debug!("None, but requires equals...Error");
|
||||
return Err(ClapError::empty_value(
|
||||
|
@ -1314,7 +1308,7 @@ where
|
|||
fn add_val_to_arg(
|
||||
&self,
|
||||
arg: &Arg<'b>,
|
||||
val: &OsStr,
|
||||
val: &ArgStr<'_>,
|
||||
matcher: &mut ArgMatcher,
|
||||
) -> ClapResult<ParseResult> {
|
||||
debug!("Parser::add_val_to_arg; arg={}, val={:?}", arg.name, val);
|
||||
|
@ -1329,13 +1323,11 @@ where
|
|||
Ok(self.add_single_val_to_arg(arg, val, matcher)?)
|
||||
} else {
|
||||
let mut iret = ParseResult::ValuesDone;
|
||||
for v in val.split(delim as u32 as u8) {
|
||||
iret = self.add_single_val_to_arg(arg, v, matcher)?;
|
||||
for v in val.split(delim) {
|
||||
iret = self.add_single_val_to_arg(arg, &v, matcher)?;
|
||||
}
|
||||
// If there was a delimiter used, we're not looking for more values
|
||||
if val.contains_byte(delim as u32 as u8)
|
||||
|| arg.is_set(ArgSettings::RequireDelimiter)
|
||||
{
|
||||
if val.contains_char(delim) || arg.is_set(ArgSettings::RequireDelimiter) {
|
||||
iret = ParseResult::ValuesDone;
|
||||
}
|
||||
Ok(iret)
|
||||
|
@ -1351,7 +1343,7 @@ where
|
|||
fn add_single_val_to_arg(
|
||||
&self,
|
||||
arg: &Arg<'b>,
|
||||
v: &OsStr,
|
||||
v: &ArgStr<'_>,
|
||||
matcher: &mut ArgMatcher,
|
||||
) -> ClapResult<ParseResult> {
|
||||
debug!("Parser::add_single_val_to_arg: adding val...{:?}", v);
|
||||
|
@ -1366,12 +1358,12 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
matcher.add_val_to(&arg.id, v);
|
||||
matcher.add_val_to(&arg.id, v.to_os_string());
|
||||
matcher.add_index_to(&arg.id, self.cur_idx.get());
|
||||
|
||||
// Increment or create the group "args"
|
||||
for grp in groups_for_arg!(self.app, arg.id) {
|
||||
matcher.add_val_to(&grp, v);
|
||||
matcher.add_val_to(&grp, v.to_os_string());
|
||||
}
|
||||
|
||||
if matcher.needs_more_vals(arg) {
|
||||
|
@ -1501,7 +1493,7 @@ where
|
|||
};
|
||||
|
||||
if add {
|
||||
self.add_val_to_arg(arg, OsStr::new(default), matcher)?;
|
||||
self.add_val_to_arg(arg, &ArgStr::new(default), matcher)?;
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
|
@ -1529,7 +1521,7 @@ where
|
|||
);
|
||||
|
||||
for val in vals {
|
||||
self.add_val_to_arg(arg, val, matcher)?;
|
||||
self.add_val_to_arg(arg, &ArgStr::new(val), matcher)?;
|
||||
}
|
||||
} else if matcher.get(&arg.id).is_some() {
|
||||
debug!("Parser::add_value:iter:{}: has user defined vals", arg.name);
|
||||
|
@ -1539,7 +1531,7 @@ where
|
|||
debug!("Parser::add_value:iter:{}: wasn't used", arg.name);
|
||||
|
||||
for val in vals {
|
||||
self.add_val_to_arg(arg, val, matcher)?;
|
||||
self.add_val_to_arg(arg, &ArgStr::new(val), matcher)?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -1560,7 +1552,7 @@ where
|
|||
if matcher.get(&a.id).map_or(true, |a| a.occurs == 0) {
|
||||
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, &ArgStr::new(val), matcher)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,10 +7,7 @@ mod strext;
|
|||
|
||||
pub use self::fnv::Key;
|
||||
|
||||
pub(crate) use self::{graph::ChildGraph, id::Id, map::VecMap, osstringext::OsStrExt2};
|
||||
|
||||
#[cfg(any(target_os = "windows", target_arch = "wasm32"))]
|
||||
pub(crate) use self::osstringext::OsStrExt3;
|
||||
pub(crate) use self::{graph::ChildGraph, id::Id, map::VecMap, osstringext::ArgStr};
|
||||
|
||||
#[cfg(feature = "color")]
|
||||
pub(crate) use termcolor;
|
||||
|
|
|
@ -1,83 +1,77 @@
|
|||
#[cfg(any(target_os = "windows", target_arch = "wasm32"))]
|
||||
use crate::INVALID_UTF8;
|
||||
use std::ffi::OsStr;
|
||||
#[cfg(not(any(target_os = "windows", target_arch = "wasm32")))]
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use std::borrow::Cow;
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::str;
|
||||
|
||||
#[cfg(any(target_os = "windows", target_arch = "wasm32"))]
|
||||
pub(crate) trait OsStrExt3 {
|
||||
fn from_bytes(b: &[u8]) -> &Self;
|
||||
fn as_bytes(&self) -> &[u8];
|
||||
}
|
||||
use os_str_bytes::{OsStrBytes, OsStringBytes};
|
||||
|
||||
pub(crate) trait OsStrExt2 {
|
||||
fn starts_with(&self, s: &[u8]) -> bool;
|
||||
fn split_at_byte(&self, b: u8) -> (&OsStr, &OsStr);
|
||||
fn split_at(&self, i: usize) -> (&OsStr, &OsStr);
|
||||
fn trim_start_matches(&self, b: u8) -> &OsStr;
|
||||
fn trim_start_n_matches(&self, n: usize, ch: u8) -> &OsStr;
|
||||
fn contains_byte(&self, b: u8) -> bool;
|
||||
fn split(&self, b: u8) -> OsSplit;
|
||||
}
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ArgStr<'a>(Cow<'a, [u8]>);
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
impl OsStrExt3 for OsStr {
|
||||
#[allow(clippy::transmute_ptr_to_ptr)]
|
||||
fn from_bytes(b: &[u8]) -> &Self {
|
||||
use std::mem;
|
||||
unsafe { mem::transmute(b) }
|
||||
}
|
||||
fn as_bytes(&self) -> &[u8] {
|
||||
self.to_str().map(|s| s.as_bytes()).expect(INVALID_UTF8)
|
||||
}
|
||||
}
|
||||
|
||||
impl OsStrExt2 for OsStr {
|
||||
fn starts_with(&self, s: &[u8]) -> bool {
|
||||
self.as_bytes().starts_with(s)
|
||||
impl<'a> ArgStr<'a> {
|
||||
pub(crate) fn new(s: &'a OsStr) -> Self {
|
||||
Self(s.to_bytes())
|
||||
}
|
||||
|
||||
fn contains_byte(&self, byte: u8) -> bool {
|
||||
for b in self.as_bytes() {
|
||||
if b == &byte {
|
||||
pub(crate) fn starts_with(&self, s: &str) -> bool {
|
||||
self.0.starts_with(s.as_bytes())
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
fn split_at_byte(&self, byte: u8) -> (&OsStr, &OsStr) {
|
||||
for (i, b) in self.as_bytes().iter().enumerate() {
|
||||
pub(crate) fn split_at_byte(&self, byte: u8) -> (ArgStr<'_>, ArgStr<'_>) {
|
||||
assert!(byte.is_ascii());
|
||||
|
||||
for (i, b) in self.0.iter().enumerate() {
|
||||
if b == &byte {
|
||||
return (
|
||||
OsStr::from_bytes(&self.as_bytes()[..i]),
|
||||
OsStr::from_bytes(&self.as_bytes()[i..]),
|
||||
);
|
||||
return self.split_at_unchecked(i);
|
||||
}
|
||||
}
|
||||
(&*self, OsStr::from_bytes(&[]))
|
||||
(self.to_borrowed(), Self(Cow::Borrowed(&[])))
|
||||
}
|
||||
|
||||
fn trim_start_matches(&self, byte: u8) -> &OsStr {
|
||||
pub(crate) fn trim_start_matches(&self, byte: u8) -> ArgStr<'_> {
|
||||
assert!(byte.is_ascii());
|
||||
|
||||
let mut found = false;
|
||||
for (i, b) in self.as_bytes().iter().enumerate() {
|
||||
for (i, b) in self.0.iter().enumerate() {
|
||||
if b != &byte {
|
||||
return OsStr::from_bytes(&self.as_bytes()[i..]);
|
||||
return Self(Cow::Borrowed(&self.0[i..]));
|
||||
} else {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if found {
|
||||
return OsStr::from_bytes(&self.as_bytes()[self.len()..]);
|
||||
return Self(Cow::Borrowed(&[]));
|
||||
}
|
||||
&*self
|
||||
self.to_borrowed()
|
||||
}
|
||||
|
||||
// Like `trim_start_matches`, but trims no more than `n` matches
|
||||
#[inline]
|
||||
fn trim_start_n_matches(&self, n: usize, ch: u8) -> &OsStr {
|
||||
pub(crate) fn trim_start_n_matches(&self, n: usize, ch: u8) -> ArgStr<'_> {
|
||||
assert!(ch.is_ascii());
|
||||
|
||||
let i = self
|
||||
.as_bytes()
|
||||
.0
|
||||
.iter()
|
||||
.take(n)
|
||||
.take_while(|c| **c == ch)
|
||||
|
@ -86,48 +80,101 @@ impl OsStrExt2 for OsStr {
|
|||
.map(|(i, _)| i + 1)
|
||||
.unwrap_or(0);
|
||||
|
||||
self.split_at(i).1
|
||||
self.split_at_unchecked(i).1
|
||||
}
|
||||
|
||||
fn split_at(&self, i: usize) -> (&OsStr, &OsStr) {
|
||||
pub(crate) fn split_at_unchecked(&self, i: usize) -> (ArgStr<'_>, ArgStr<'_>) {
|
||||
(
|
||||
OsStr::from_bytes(&self.as_bytes()[..i]),
|
||||
OsStr::from_bytes(&self.as_bytes()[i..]),
|
||||
Self(Cow::Borrowed(&self.0[..i])),
|
||||
Self(Cow::Borrowed(&self.0[i..])),
|
||||
)
|
||||
}
|
||||
|
||||
fn split(&self, b: u8) -> OsSplit {
|
||||
OsSplit {
|
||||
sep: b,
|
||||
val: self.as_bytes(),
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn as_raw_bytes(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
|
||||
pub(crate) fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
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 {
|
||||
OsString::from_bytes(&self.0).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
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 OsSplit<'a> {
|
||||
sep: u8,
|
||||
pub(crate) struct ArgSplit<'a> {
|
||||
sep: [u8; 4],
|
||||
sep_len: usize,
|
||||
val: &'a [u8],
|
||||
pos: usize,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for OsSplit<'a> {
|
||||
type Item = &'a OsStr;
|
||||
impl<'a> Iterator for ArgSplit<'a> {
|
||||
type Item = ArgStr<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<&'a OsStr> {
|
||||
debug!("OsSplit::next: self={:?}", self);
|
||||
fn next(&mut self) -> Option<ArgStr<'a>> {
|
||||
debug!("ArgSplit::next: self={:?}", self);
|
||||
|
||||
if self.pos == self.val.len() {
|
||||
return None;
|
||||
}
|
||||
let start = self.pos;
|
||||
for b in &self.val[start..] {
|
||||
self.pos += 1;
|
||||
if *b == self.sep {
|
||||
return Some(OsStr::from_bytes(&self.val[start..self.pos - 1]));
|
||||
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(OsStr::from_bytes(&self.val[start..]))
|
||||
Some(ArgStr(Cow::Borrowed(&self.val[start..])))
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue