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"
textwrap = { version = "0.14.0", default-features = false, features = [] }
indexmap = "1.0"
os_str_bytes = { version = "3.0", features = ["raw"] }
os_str_bytes = "4.1"
vec_map = "0.8"
strsim = { version = "0.10", optional = true }
yaml-rust = { version = "0.4.1", optional = true }

View file

@ -18,6 +18,7 @@ use std::{
};
// Third Party
use os_str_bytes::RawOsStr;
#[cfg(feature = "yaml")]
use yaml_rust::Yaml;
@ -27,7 +28,7 @@ use crate::{
mkeymap::MKeyMap,
output::{fmt::Colorizer, Help, HelpWriter, Usage},
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,
};
@ -2791,7 +2792,7 @@ impl<'help> App<'help> {
}
/// 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()
.find(|sc| sc.long_flag_aliases_to(long))
.map(|sc| sc.get_name())

View file

@ -1,6 +1,6 @@
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)]
pub(crate) struct Key {
@ -49,8 +49,8 @@ impl PartialEq<&str> for KeyType {
}
}
impl PartialEq<OsString> for KeyType {
fn eq(&self, rhs: &OsString) -> bool {
impl PartialEq<OsStr> for KeyType {
fn eq(&self, rhs: &OsStr) -> bool {
match self {
KeyType::Long(l) => l == rhs,
_ => false,
@ -86,7 +86,7 @@ impl<'help> MKeyMap<'help> {
/// 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
/// 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
KeyType: PartialEq<K>,
{

View file

@ -4,6 +4,9 @@ use std::{
ffi::{OsStr, OsString},
};
// Third Party
use os_str_bytes::RawOsStr;
// Internal
use crate::{
build::AppSettings as AS,
@ -16,7 +19,7 @@ use crate::{
parse::features::suggestions,
parse::{ArgMatcher, SubCommand},
parse::{Validator, ValueType},
util::{termcolor::ColorChoice, ArgStr, ChildGraph, Id},
util::{termcolor::ColorChoice, ChildGraph, Id},
INTERNAL_ERROR_MSG, INVALID_UTF8,
};
@ -374,7 +377,7 @@ impl<'help, 'app> Parser<'help, 'app> {
continue;
}
let arg_os = ArgStr::new(arg_os);
let arg_os = RawOsStr::new(arg_os);
debug!(
"Parser::get_matches_with: Begin parsing '{:?}' ({:?})",
arg_os,
@ -423,7 +426,7 @@ impl<'help, 'app> Parser<'help, 'app> {
// pos_counter(which means current value cannot be a
// positional argument with a value next to it), assume
// current value matches the next arg.
let n = ArgStr::new(n);
let n = RawOsStr::new(n);
self.is_new_arg(&n, p)
|| self.possible_subcommand(&n, valid_arg_found).is_some()
} 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(
matcher,
&arg_os,
long_arg,
&parse_state,
&mut valid_arg_found,
trailing_values,
@ -537,7 +540,7 @@ impl<'help, 'app> Parser<'help, 'app> {
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
// 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)
let parse_result = self.parse_short_arg(
matcher,
&arg_os,
short_arg,
&parse_state,
pos_counter,
&mut valid_arg_found,
@ -626,7 +629,7 @@ impl<'help, 'app> Parser<'help, 'app> {
// get the option so we can check the settings
let parse_result = self.add_val_to_arg(
&self.app[id],
arg_os,
&arg_os,
matcher,
ValueType::CommandLine,
true,
@ -645,7 +648,7 @@ impl<'help, 'app> Parser<'help, 'app> {
if let Some(p) = self.app.args.get(&pos_counter) {
if p.is_set(ArgSettings::Last) && !trailing_values {
return Err(ClapError::unknown_argument(
arg_os.to_string_lossy().to_string(),
arg_os.to_str_lossy().into_owned(),
None,
Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
@ -663,7 +666,7 @@ impl<'help, 'app> Parser<'help, 'app> {
let append = self.arg_have_val(matcher, p);
self.add_val_to_arg(
p,
arg_os,
&arg_os,
matcher,
ValueType::CommandLine,
append,
@ -768,7 +771,7 @@ impl<'help, 'app> Parser<'help, 'app> {
fn match_arg_error(
&self,
arg_os: &ArgStr,
arg_os: &RawOsStr,
valid_arg_found: bool,
trailing_values: bool,
) -> 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 self.possible_subcommand(arg_os, valid_arg_found).is_some() {
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(&[]),
self.app.color(),
);
}
}
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 !candidates.is_empty() {
let candidates: Vec<_> = candidates
@ -792,7 +795,7 @@ impl<'help, 'app> Parser<'help, 'app> {
.map(|candidate| format!("'{}'", candidate))
.collect();
return ClapError::invalid_subcommand(
arg_os.to_string_lossy().to_string(),
arg_os.to_str_lossy().into_owned(),
candidates.join(" or "),
self.app
.bin_name
@ -806,7 +809,7 @@ impl<'help, 'app> Parser<'help, 'app> {
// If the argument must be a subcommand.
if !self.app.has_args() || self.is_set(AS::InferSubcommands) && self.app.has_subcommands() {
return ClapError::unrecognized_subcommand(
arg_os.to_string_lossy().to_string(),
arg_os.to_str_lossy().into_owned(),
self.app
.bin_name
.as_ref()
@ -816,7 +819,7 @@ impl<'help, 'app> Parser<'help, 'app> {
);
}
ClapError::unknown_argument(
arg_os.to_string_lossy().to_string(),
arg_os.to_str_lossy().into_owned(),
None,
Usage::new(self).create_usage_with_title(&[]),
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)
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);
if !(self.is_set(AS::ArgsNegateSubcommands) && valid_arg_found) {
@ -845,7 +848,7 @@ impl<'help, 'app> Parser<'help, 'app> {
let v = self
.app
.all_subcommand_names()
.filter(|s| arg_os.is_prefix_of(s))
.filter(|s| RawOsStr::from_str(s).starts_with_os(arg_os))
.collect::<Vec<_>>();
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)
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);
if self.is_set(AS::InferSubcommands) {
let options = self
@ -871,12 +874,12 @@ impl<'help, 'app> Parser<'help, 'app> {
.get_subcommands()
.fold(Vec::new(), |mut options, sc| {
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.extend(
sc.get_all_aliases()
.filter(|alias| arg_os.is_prefix_of(alias)),
.filter(|alias| RawOsStr::from_str(alias).starts_with_os(arg_os)),
)
}
options
@ -885,7 +888,7 @@ impl<'help, 'app> Parser<'help, 'app> {
return Some(options[0]);
}
for sc in &options {
for sc in options {
if sc == arg_os {
return Some(sc);
}
@ -930,7 +933,7 @@ impl<'help, 'app> Parser<'help, 'app> {
}
} else {
return Err(ClapError::unrecognized_subcommand(
cmd.to_string_lossy().to_string(),
cmd.to_string_lossy().into_owned(),
self.app
.bin_name
.as_ref()
@ -968,7 +971,7 @@ impl<'help, 'app> Parser<'help, 'app> {
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!(
"Parser::is_new_arg: {:?}:{:?}",
next, current_positional.name
@ -977,7 +980,7 @@ impl<'help, 'app> Parser<'help, 'app> {
if self.is_set(AS::AllowLeadingHyphen)
|| self.app[&current_positional.id].is_set(ArgSettings::AllowHyphenValues)
|| (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.
debug!("Parser::is_new_arg: Allow hyphen");
@ -990,7 +993,7 @@ impl<'help, 'app> Parser<'help, 'app> {
debug!("Parser::is_new_arg: - found");
// 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.
next.len() != 1
next.raw_len() != 1
} else {
debug!("Parser::is_new_arg: 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
// 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: Checking if --{:?} is help or version...",
@ -1172,7 +1175,7 @@ impl<'help, 'app> Parser<'help, 'app> {
fn parse_long_arg(
&mut self,
matcher: &mut ArgMatcher,
full_arg: &ArgStr,
long_arg: &RawOsStr,
parse_state: &ParseState,
valid_arg_found: &mut 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: Does it contain '='...");
let long_arg = full_arg.trim_start_n_matches(2, b'-');
if long_arg.is_empty() {
return ParseResult::NoArg;
}
let (arg, val) = if full_arg.contains_byte(b'=') {
let (p0, p1) = long_arg.split_at_byte(b'=');
let (arg, val) = if let Some(index) = long_arg.find("=") {
let (p0, p1) = long_arg.split_at(index);
debug!("Yes '{:?}'", p1);
(p0, Some(p1))
} else {
@ -1204,20 +1206,20 @@ impl<'help, 'app> Parser<'help, 'app> {
(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!(
"Parser::parse_long_arg: Found valid opt or flag '{}'",
opt.to_string()
);
Some(opt)
} 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| {
a.long
.map_or(false, |long| long.starts_with(arg_str.as_ref()))
.map_or(false, |long| long.starts_with(&*arg_str))
|| a.aliases
.iter()
.any(|(alias, _)| alias.starts_with(arg_str.as_ref()))
.any(|(alias, _)| alias.starts_with(&*arg_str))
})
} else {
None
@ -1231,7 +1233,7 @@ impl<'help, 'app> Parser<'help, 'app> {
"Parser::parse_long_arg: Found an opt with value '{:?}'",
&val
);
self.parse_opt(&val, opt, matcher, trailing_values)
self.parse_opt(val, opt, matcher, trailing_values)
} else if let Some(rest) = val {
debug!(
"Parser::parse_long_arg: Got invalid literal `{:?}`",
@ -1248,7 +1250,7 @@ impl<'help, 'app> Parser<'help, 'app> {
.collect();
ParseResult::UnneededAttachedValue {
rest: rest.to_string_lossy().to_string(),
rest: rest.to_str_lossy().into_owned(),
used,
arg: opt.to_string(),
}
@ -1264,7 +1266,7 @@ impl<'help, 'app> Parser<'help, 'app> {
ParseResult::MaybeHyphenValue
} else {
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(
&mut self,
matcher: &mut ArgMatcher,
full_arg: &ArgStr,
short_arg: &RawOsStr,
parse_state: &ParseState,
// change this to possible pos_arg when removing the usage of &mut Parser.
pos_counter: usize,
valid_arg_found: &mut bool,
trailing_values: bool,
) -> ParseResult {
debug!("Parser::parse_short_arg: full_arg={:?}", full_arg);
let arg_os = full_arg.trim_start_matches(b'-');
let arg = arg_os.to_string_lossy();
debug!("Parser::parse_short_arg: short_arg={:?}", short_arg);
let arg = short_arg.to_str_lossy();
if (self.is_set(AS::AllowNegativeNumbers) && arg.parse::<f64>().is_ok())
|| (self.is_set(AS::AllowLeadingHyphen)
@ -1332,24 +1333,12 @@ impl<'help, 'app> Parser<'help, 'app> {
}
// 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!(
"Parser::parse_short_arg:iter:{}: i={}, arg_os={:?}",
c, i, arg_os
"Parser::parse_short_arg:iter:{}: val={:?} (bytes), val={:?} (ascii), rest={:?}, arg_os={:?}",
c, val, val.as_raw_bytes(), arg_os
);
let val = if i != arg_os.len() {
// 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
};
let val = Some(val).filter(|v| !v.is_empty());
// 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
// 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,
x => return x,
}
@ -1393,7 +1382,7 @@ impl<'help, 'app> Parser<'help, 'app> {
fn parse_opt(
&self,
attached_value: &Option<ArgStr>,
attached_value: Option<&RawOsStr>,
opt: &Arg<'help>,
matcher: &mut ArgMatcher,
trailing_values: bool,
@ -1435,8 +1424,8 @@ impl<'help, 'app> Parser<'help, 'app> {
}
}
} else if let Some(fv) = attached_value {
let v = fv.trim_start_n_matches(1, b'=');
debug!("Found - {:?}, len: {}", v, v.len());
let v = fv.strip_prefix("=").unwrap_or(fv);
debug!("Found - {:?}, len: {}", v, v.raw_len());
debug!(
"Parser::parse_opt: {:?} contains '='...{:?}",
fv,
@ -1466,7 +1455,7 @@ impl<'help, 'app> Parser<'help, 'app> {
fn add_val_to_arg(
&self,
arg: &Arg<'help>,
val: ArgStr,
val: &RawOsStr,
matcher: &mut ArgMatcher,
ty: ValueType,
append: bool,
@ -1495,7 +1484,7 @@ impl<'help, 'app> Parser<'help, 'app> {
};
self.add_multiple_vals_to_arg(
arg,
vals.into_iter().map(|x| x.into_os_string()),
vals.into_iter().map(|x| x.to_os_str().into_owned()),
matcher,
ty,
append,
@ -1503,7 +1492,7 @@ impl<'help, 'app> Parser<'help, 'app> {
// 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
// looking for more values.
return if val.contains_char(delim)
return if val.contains(delim)
|| arg.is_set(ArgSettings::RequireDelimiter)
|| !matcher.needs_more_vals(arg)
{
@ -1518,7 +1507,7 @@ impl<'help, 'app> Parser<'help, 'app> {
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) {
ParseResult::Opt(arg.id.clone())
} else {
@ -1686,7 +1675,7 @@ impl<'help, 'app> Parser<'help, 'app> {
if let Some(default) = default {
self.add_val_to_arg(
arg,
ArgStr::new(default),
&RawOsStr::new(default),
matcher,
ty,
false,
@ -1782,7 +1771,7 @@ impl<'help, 'app> Parser<'help, 'app> {
debug!("Parser::add_env: Checking arg `{}`", a);
if let Some((_, Some(ref val))) = a.env {
let val = ArgStr::new(val);
let val = RawOsStr::new(val);
if a.is_set(ArgSettings::TakesValue) {
debug!(
@ -1791,7 +1780,7 @@ impl<'help, 'app> Parser<'help, 'app> {
);
self.add_val_to_arg(
a,
val,
&val,
matcher,
ValueType::EnvVariable,
false,
@ -1813,7 +1802,7 @@ impl<'help, 'app> Parser<'help, 'app> {
}
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);
if predicate {
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()
.collect();
return Err(Error::invalid_value(
val_str.to_string(),
val_str.into_owned(),
&arg.possible_vals,
arg,
Usage::new(self.p).create_usage_with_title(&used),
@ -149,7 +149,7 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> {
debug!("error");
return Err(Error::value_validation(
arg.to_string(),
val.to_string_lossy().to_string(),
val.to_string_lossy().into_owned(),
e,
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)]
mod argstr;
mod fnv;
mod graph;
mod id;
@ -11,7 +10,7 @@ pub use self::fnv::Key;
#[cfg(feature = "env")]
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;
#[cfg(feature = "color")]