use crate::PathStyle;
use std::ffi::OsStr;
use std::path::Path;
pub use std::path::Prefix;
pub(crate) trait PrefixExt {
fn len(&self) -> usize;
fn is_drive(&self) -> bool;
#[inline]
fn has_implicit_root(&self) -> bool {
!self.is_drive()
}
}
impl PrefixExt for Prefix<'_> {
#[inline]
fn len(&self) -> usize {
use self::Prefix::*;
fn os_str_len(s: &OsStr) -> usize {
s.as_encoded_bytes().len()
}
match *self {
Verbatim(x) => 4 + os_str_len(x),
VerbatimUNC(x, y) => {
8 + os_str_len(x)
+ if os_str_len(y) > 0 {
1 + os_str_len(y)
} else {
0
}
}
VerbatimDisk(_) => 6,
UNC(x, y) => {
2 + os_str_len(x)
+ if os_str_len(y) > 0 {
1 + os_str_len(y)
} else {
0
}
}
DeviceNS(x) => 4 + os_str_len(x),
Disk(_) => 2,
}
}
#[inline]
fn is_drive(&self) -> bool {
matches!(*self, Prefix::Disk(_))
}
}
pub(crate) fn iter_after<'a, 'b, I, J>(mut iter: I, mut prefix: J) -> Option<I>
where
I: Iterator<Item = Component<'a>> + Clone,
J: Iterator<Item = Component<'b>>,
{
loop {
let mut iter_next = iter.clone();
match (iter_next.next(), prefix.next()) {
(Some(ref x), Some(ref y)) if x == y => (),
(Some(_), Some(_)) => return None,
(Some(_), None) => return Some(iter),
(None, None) => return Some(iter),
(None, Some(_)) => return None,
}
iter = iter_next;
}
}
#[derive(PartialEq, PartialOrd, Clone)]
enum State {
Prefix = 0, StartDir = 1, Body = 2, Done = 3,
}
#[allow(dead_code)]
pub(crate) struct PrefixComponent<'a> {
raw: &'a OsStr,
parsed: Prefix<'a>,
}
impl<'a> PartialEq for PrefixComponent<'a> {
#[inline]
fn eq(&self, other: &PrefixComponent<'a>) -> bool {
self.parsed == other.parsed
}
}
#[derive(PartialEq)]
pub(crate) enum Component<'a> {
Prefix(PrefixComponent<'a>),
RootDir,
CurDir,
ParentDir,
Normal(&'a OsStr),
}
#[derive(Clone)]
pub(crate) struct Components<'a> {
path_style: PathStyle,
path: &'a [u8],
prefix: Option<Prefix<'a>>,
has_physical_root: bool,
front: State,
back: State,
}
impl<'a> Components<'a> {
pub fn new(path_style: PathStyle, path: &'a Path) -> Self {
let prefix = path_style.parse_prefix(path.as_os_str());
Components {
path_style,
path: path.as_os_str().as_encoded_bytes(),
prefix,
has_physical_root: path_style
.has_physical_root(path.as_os_str().as_encoded_bytes(), prefix),
front: if path_style.has_prefixes() {
State::Prefix
} else {
State::StartDir
},
back: State::Body,
}
}
fn has_prefixes(&self) -> bool {
self.path_style.has_prefixes()
}
#[inline]
fn prefix_len(&self) -> usize {
if !self.has_prefixes() {
return 0;
}
self.prefix.as_ref().map(Prefix::len).unwrap_or(0)
}
#[inline]
fn prefix_verbatim(&self) -> bool {
if !self.has_prefixes() {
return false;
}
self.prefix
.as_ref()
.map(Prefix::is_verbatim)
.unwrap_or(false)
}
#[inline]
fn prefix_remaining(&self) -> usize {
if !self.has_prefixes() {
return 0;
}
if self.front == State::Prefix {
self.prefix_len()
} else {
0
}
}
#[inline]
fn len_before_body(&self) -> usize {
let root = if self.front <= State::StartDir && self.has_physical_root {
1
} else {
0
};
let cur_dir = if self.front <= State::StartDir && self.include_cur_dir() {
1
} else {
0
};
self.prefix_remaining() + root + cur_dir
}
#[inline]
fn finished(&self) -> bool {
self.front == State::Done || self.back == State::Done || self.front > self.back
}
#[inline]
fn is_sep_byte(&self, b: u8) -> bool {
if self.prefix_verbatim() {
self.path_style.is_verbatim_sep(b)
} else {
self.path_style.is_sep_byte(b)
}
}
pub fn as_path(&self) -> &'a Path {
let mut comps = self.clone();
if comps.front == State::Body {
comps.trim_left();
}
if comps.back == State::Body {
comps.trim_right();
}
unsafe { Path::new(OsStr::from_encoded_bytes_unchecked(comps.path)) }
}
fn has_root(&self) -> bool {
if self.has_physical_root {
return true;
}
if self.has_prefixes()
&& let Some(p) = self.prefix
{
if p.has_implicit_root() {
return true;
}
}
false
}
fn include_cur_dir(&self) -> bool {
if self.has_root() {
return false;
}
let slice = &self.path[self.prefix_remaining()..];
match slice {
[b'.'] => true,
[b'.', b, ..] => self.is_sep_byte(*b),
_ => false,
}
}
unsafe fn parse_single_component<'b>(&self, comp: &'b [u8]) -> Option<Component<'b>> {
match comp {
b"." if self.has_prefixes() && self.prefix_verbatim() => Some(Component::CurDir),
b"." => None, b".." => Some(Component::ParentDir),
b"" => None,
_ => Some(Component::Normal(unsafe {
OsStr::from_encoded_bytes_unchecked(comp)
})),
}
}
fn parse_next_component(&self) -> (usize, Option<Component<'a>>) {
debug_assert!(self.front == State::Body);
let (extra, comp) = match self.path.iter().position(|b| self.is_sep_byte(*b)) {
None => (0, self.path),
Some(i) => (1, &self.path[..i]),
};
(comp.len() + extra, unsafe {
self.parse_single_component(comp)
})
}
fn parse_next_component_back(&self) -> (usize, Option<Component<'a>>) {
debug_assert!(self.back == State::Body);
let start = self.len_before_body();
let (extra, comp) = match self.path[start..]
.iter()
.rposition(|b| self.is_sep_byte(*b))
{
None => (0, &self.path[start..]),
Some(i) => (1, &self.path[start + i + 1..]),
};
(comp.len() + extra, unsafe {
self.parse_single_component(comp)
})
}
fn trim_left(&mut self) {
while !self.path.is_empty() {
let (size, comp) = self.parse_next_component();
if comp.is_some() {
return;
} else {
self.path = &self.path[size..];
}
}
}
fn trim_right(&mut self) {
while self.path.len() > self.len_before_body() {
let (size, comp) = self.parse_next_component_back();
if comp.is_some() {
return;
} else {
self.path = &self.path[..self.path.len() - size];
}
}
}
}
impl<'a> Iterator for Components<'a> {
type Item = Component<'a>;
fn next(&mut self) -> Option<Component<'a>> {
while !self.finished() {
match self.front {
State::Body if !self.path.is_empty() => {
let (size, comp) = self.parse_next_component();
self.path = &self.path[size..];
if comp.is_some() {
return comp;
}
}
State::Body => {
self.front = State::Done;
}
State::StartDir => {
self.front = State::Body;
if self.has_physical_root {
debug_assert!(!self.path.is_empty());
self.path = &self.path[1..];
return Some(Component::RootDir);
} else if self.has_prefixes()
&& let Some(p) = self.prefix
{
if p.has_implicit_root() && !p.is_verbatim() {
return Some(Component::RootDir);
}
} else if self.include_cur_dir() {
debug_assert!(!self.path.is_empty());
self.path = &self.path[1..];
return Some(Component::CurDir);
}
}
_ if !self.has_prefixes() => unreachable!(),
State::Prefix if self.prefix_len() == 0 => {
self.front = State::StartDir;
}
State::Prefix => {
self.front = State::StartDir;
debug_assert!(self.prefix_len() <= self.path.len());
let raw = &self.path[..self.prefix_len()];
self.path = &self.path[self.prefix_len()..];
return Some(Component::Prefix(PrefixComponent {
raw: unsafe { OsStr::from_encoded_bytes_unchecked(raw) },
parsed: self.prefix.unwrap(),
}));
}
State::Done => unreachable!(),
}
}
None
}
}
impl<'a> DoubleEndedIterator for Components<'a> {
fn next_back(&mut self) -> Option<Component<'a>> {
while !self.finished() {
match self.back {
State::Body if self.path.len() > self.len_before_body() => {
let (size, comp) = self.parse_next_component_back();
self.path = &self.path[..self.path.len() - size];
if comp.is_some() {
return comp;
}
}
State::Body => {
self.back = State::StartDir;
}
State::StartDir => {
self.back = if self.has_prefixes() {
State::Prefix
} else {
State::Done
};
if self.has_physical_root {
self.path = &self.path[..self.path.len() - 1];
return Some(Component::RootDir);
} else if self.has_prefixes()
&& let Some(p) = self.prefix
{
if p.has_implicit_root() && !p.is_verbatim() {
return Some(Component::RootDir);
}
} else if self.include_cur_dir() {
self.path = &self.path[..self.path.len() - 1];
return Some(Component::CurDir);
}
}
_ if !self.has_prefixes() => unreachable!(),
State::Prefix if self.prefix_len() > 0 => {
self.back = State::Done;
return Some(Component::Prefix(PrefixComponent {
raw: unsafe { OsStr::from_encoded_bytes_unchecked(self.path) },
parsed: self.prefix.unwrap(),
}));
}
State::Prefix => {
self.back = State::Done;
return None;
}
State::Done => unreachable!(),
}
}
None
}
}