use std::{
borrow::Cow,
ffi::{OsStr, OsString},
fmt::{self, Debug},
str,
};
use os_str_bytes::{raw, OsStrBytes, OsStringBytes};
pub(crate) struct ArgStr<'a>(Cow<'a, [u8]>);
impl<'a> ArgStr<'a> {
pub(crate) fn new(s: &'a OsStr) -> Self {
Self(s.to_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());
for (i, b) in self.0.iter().enumerate() {
if b == &byte {
return self.split_at_unchecked(i);
}
}
(self.to_borrowed(), Self(Cow::Borrowed(&[])))
}
pub(crate) fn trim_start_matches(&'a self, byte: u8) -> ArgStr {
assert!(byte.is_ascii());
let mut found = false;
for (i, b) in self.0.iter().enumerate() {
if b != &byte {
return Self(Cow::Borrowed(&self.0[i..]));
} else {
found = true;
}
}
if found {
return Self(Cow::Borrowed(&[]));
}
self.to_borrowed()
}
#[inline]
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()
}
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> 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..])))
}
}