use crate::{Component, Components, String, ToOwned};
const CURRENT_DIR: &str = ".";
const PARENT_DIR: &str = "..";
#[inline]
fn rsplit_once_with_delimiter<'i>(
s: &'i str,
delimiter: &[char],
) -> Option<(&'i str, &'i str, &'i str)> {
let i = s.rfind(delimiter)?;
let (a, b) = s.split_at(i);
let (b, c) = b.split_at(1);
Some((a, b, c))
}
#[inline]
fn split_once_with_delimiter<'i>(
s: &'i str,
delimiter: &[char],
) -> Option<(&'i str, &'i str, &'i str)> {
let i = s.find(delimiter)?;
let (a, b) = s.split_at(i);
let (b, c) = b.split_at(1);
Some((a, b, c))
}
pub trait ParsablePath {
const PRIMARY_COMPONENT_SEPARATOR: char;
const SECONDARY_COMPONENT_SEPARATOR: Option<char>;
const COMPONENT_SEPARATORS: &'static [char] = match Self::SECONDARY_COMPONENT_SEPARATOR {
Some(c) => &[Self::PRIMARY_COMPONENT_SEPARATOR, c],
None => &[Self::PRIMARY_COMPONENT_SEPARATOR],
};
const EXTENSION_SEPARATOR: char;
const DRIVE_SEPARATOR: Option<char>;
fn as_string_mut(&mut self) -> &mut String;
fn split_first_lexical(path: &str) -> (&str, Option<(&str, &str)>) {
match split_once_with_delimiter(path, Self::COMPONENT_SEPARATORS) {
Some((prefix, separator, component)) => (prefix, Some((separator, component))),
None => (path, None),
}
}
#[inline]
fn split_first_component(
mut s: &str,
progressed: bool,
) -> (Option<Component<'_>>, Option<&str>) {
loop {
match Self::split_first_lexical(s) {
(CURRENT_DIR, Some((_, suffix))) => match progressed {
true => {
s = suffix;
continue;
}
false => return (Some(Component::CurDir), Some(suffix)),
},
(CURRENT_DIR, None) => match progressed {
true => return (None, None),
false => return (Some(Component::CurDir), None),
},
(PARENT_DIR, Some((_, suffix))) => {
return (Some(Component::ParentDir), Some(suffix))
}
(PARENT_DIR, None) => return (Some(Component::ParentDir), None),
("", Some((_, suffix))) => match progressed {
true => return (None, Some(suffix)),
false => return (Some(Component::Root), Some(suffix)),
},
("", None) => {
return (None, None);
}
(prefix, Some((_, file_name))) => {
return (Some(Component::Normal(prefix)), Some(file_name))
}
(prefix, None) => return (Some(Component::Normal(prefix)), None),
}
}
}
fn split_last_lexical(path: &str) -> (Option<(&str, &str)>, &str) {
let s = path;
match rsplit_once_with_delimiter(s, Self::COMPONENT_SEPARATORS) {
Some((parent, separator, component)) => (Some((parent, separator)), component),
None => (None, path),
}
}
#[inline]
fn split_last_component(mut s: &str, _: bool) -> (Option<&str>, Option<Component<'_>>) {
loop {
match Self::split_last_lexical(s) {
(Some(("", _)), CURRENT_DIR) => return (Some(""), Some(Component::Root)),
(Some((parent, _)), CURRENT_DIR) => {
s = parent;
continue;
}
(None, CURRENT_DIR) => return (Some(""), Some(Component::CurDir)),
(Some(("", separator)), PARENT_DIR) => {
return (Some(separator), Some(Component::ParentDir))
}
(Some((parent, _)), PARENT_DIR) => {
return (Some(parent), Some(Component::ParentDir))
}
(None, PARENT_DIR) => return (Some(""), Some(Component::ParentDir)),
(Some(("", _)), "") => return (None, Some(Component::Root)),
(None, "") => return (None, None),
(Some((parent, _)), "") => {
s = parent;
continue;
}
(Some(("", separator)), file_name) => {
return (Some(separator), Some(Component::Normal(file_name)))
}
(Some((parent, _)), file_name) => {
return (Some(parent), Some(Component::Normal(file_name)))
}
(None, file_name) => return (Some(""), Some(Component::Normal(file_name))),
}
}
}
fn split_last(mut s: &str) -> (Option<&str>, Option<&str>) {
loop {
match Self::split_last_lexical(s) {
(Some((parent, _)), CURRENT_DIR) => {
s = parent;
continue;
}
(None, CURRENT_DIR) => return (Some(""), None),
(Some(("", separator)), PARENT_DIR) => return (Some(separator), None),
(Some((parent, _)), PARENT_DIR) => return (Some(parent), None),
(None, PARENT_DIR) => return (Some(""), None),
(Some(("", _)), "") => return (None, None),
(None, "") => return (None, None),
(Some((parent, _)), "") => {
s = parent;
continue;
}
(Some(("", separator)), file_name) => return (Some(separator), Some(file_name)),
(Some((parent, _)), file_name) => return (Some(parent), Some(file_name)),
(None, file_name) => return (Some(""), Some(file_name)),
}
}
}
fn parent(s: &str) -> Option<&str> {
Self::split_last(s).0
}
fn file_name(s: &str) -> Option<&str> {
Self::split_last(s).1
}
fn join_in_place(parent: &mut String, child: &str) {
if Self::is_absolute(child) {
parent.clear();
parent.push_str(child);
return;
}
Self::as_dir(parent);
parent.push_str(child.as_ref());
}
fn join(parent: &str, child: &str) -> String {
if Self::is_absolute(child) {
return child.to_owned();
}
let mut joined = parent.to_owned();
Self::as_dir(&mut joined);
joined.push_str(child.as_ref());
joined
}
fn split_extension(s: &str) -> (&str, Option<&str>) {
let s = Self::file_name(s).unwrap_or("");
match rsplit_once_with_delimiter(s, &[Self::EXTENSION_SEPARATOR]) {
Some((stem, _, extension)) => (stem, Some(extension)),
None => (s, None),
}
}
fn with_extension(path: &str, ext: &str) -> String {
let (path, _) = Self::split_extension(path);
let mut new = path.to_owned();
new.push(Self::EXTENSION_SEPARATOR);
new.push_str(ext);
new
}
fn split_driver(path: &str) -> (Option<&str>, &str) {
if let Some(c) = Self::DRIVE_SEPARATOR {
if let Some((drive, rest)) = path.split_once(c) {
(Some(drive), rest)
} else {
(None, path)
}
} else {
(None, path)
}
}
fn is_absolute(path: &str) -> bool {
path.starts_with(Self::COMPONENT_SEPARATORS)
}
fn as_dir(path: &mut String) {
if !path.ends_with(Self::COMPONENT_SEPARATORS) {
path.push(Self::PRIMARY_COMPONENT_SEPARATOR);
}
}
}
pub trait PurePath: Sized {
fn parent(&self) -> Option<Self>;
fn file_name(&self) -> Option<&str>;
fn join_in_place(&mut self, path: &Self);
fn join(&self, path: &Self) -> Self;
fn is_absolute(&self) -> bool;
fn components(&self) -> impl DoubleEndedIterator<Item = Component<'_>>;
}
impl<P: ParsablePath + Sized + AsRef<str> + for<'a> From<&'a str> + From<String>> PurePath for P {
fn parent(&self) -> Option<Self> {
let parent = Self::parent(self.as_ref());
parent.map(Self::from)
}
fn file_name(&self) -> Option<&str> {
Self::file_name(self.as_ref())
}
fn join_in_place(&mut self, path: &Self) {
Self::join_in_place(self.as_string_mut(), path.as_ref());
}
fn join(&self, path: &Self) -> Self {
let joined = Self::join(self.as_ref(), path.as_ref());
Self::from(joined)
}
fn is_absolute(&self) -> bool {
Self::is_absolute(self.as_ref())
}
fn components(&self) -> impl DoubleEndedIterator<Item = Component<'_>> {
<Components<'_, Self>>::new(self.as_ref())
}
}