use crate::{
chars::Chars,
pack::{Pack, PackError},
utils,
};
use arcstr::ArcStr;
use bytes::{Buf, BufMut};
use std::{
borrow::{Borrow, Cow},
cmp::{Eq, Ord, PartialEq, PartialOrd},
convert::{AsRef, From},
fmt,
iter::{DoubleEndedIterator, Iterator},
ops::Deref,
result::Result,
str::{self, FromStr},
};
pub static ESC: char = '\\';
pub static SEP: char = '/';
fn is_canonical(s: &str) -> bool {
for _ in Path::parts(s).filter(|p| *p == "") {
return false;
}
true
}
fn canonize(s: &str) -> String {
let mut res = String::with_capacity(s.len());
if s.len() > 0 {
if s.starts_with(SEP) {
res.push(SEP)
}
let mut first = true;
for p in Path::parts(s).filter(|p| *p != "") {
if first {
first = false;
} else {
res.push(SEP)
}
res.push_str(p);
}
}
res
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Path(ArcStr);
impl Pack for Path {
fn encoded_len(&self) -> usize {
<ArcStr as Pack>::encoded_len(&self.0)
}
fn encode(&self, buf: &mut impl BufMut) -> Result<(), PackError> {
<ArcStr as Pack>::encode(&self.0, buf)
}
fn decode(buf: &mut impl Buf) -> Result<Self, PackError> {
Ok(Path::from(<ArcStr as Pack>::decode(buf)?))
}
}
impl fmt::Display for Path {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl AsRef<str> for Path {
fn as_ref(&self) -> &str {
&*self.0
}
}
impl Borrow<str> for Path {
fn borrow(&self) -> &str {
&*self.0
}
}
impl Deref for Path {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<Chars> for Path {
fn from(c: Chars) -> Path {
if is_canonical(&c) {
Path(ArcStr::from(c.as_ref()))
} else {
Path(ArcStr::from(canonize(&c)))
}
}
}
impl From<String> for Path {
fn from(s: String) -> Path {
if is_canonical(&s) {
Path(ArcStr::from(s))
} else {
Path(ArcStr::from(canonize(&s)))
}
}
}
impl From<&'static str> for Path {
fn from(s: &'static str) -> Path {
if is_canonical(s) {
Path(ArcStr::from(s))
} else {
Path(ArcStr::from(canonize(s)))
}
}
}
impl<'a> From<&'a String> for Path {
fn from(s: &String) -> Path {
if is_canonical(s.as_str()) {
Path(ArcStr::from(s.clone()))
} else {
Path(ArcStr::from(canonize(s.as_str())))
}
}
}
impl From<ArcStr> for Path {
fn from(s: ArcStr) -> Path {
if is_canonical(&*s) {
Path(s)
} else {
Path(ArcStr::from(canonize(&*s)))
}
}
}
impl FromStr for Path {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if is_canonical(s) {
Ok(Path(ArcStr::from(s)))
} else {
Ok(Path(ArcStr::from(canonize(s))))
}
}
}
pub enum DirNames<'a> {
Root(bool),
Path { cur: &'a str, all: &'a str, base: usize },
}
impl<'a> Iterator for DirNames<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<Self::Item> {
match self {
DirNames::Path { ref mut cur, all, ref mut base } => {
if *base >= all.len() {
None
} else {
match Path::find_sep(cur) {
None => {
*base = all.len();
Some(all)
}
Some(p) => {
*base += p + 1;
*cur = &all[*base..];
Some(&all[0..*base - 1])
}
}
}
}
DirNames::Root(true) => {
*self = DirNames::Root(false);
Some("/")
}
DirNames::Root(false) => None,
}
}
}
impl<'a> DoubleEndedIterator for DirNames<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
match self {
DirNames::Path { cur: _, ref mut all, base: _ } => {
match Path::dirname(*all) {
Some(dn) => {
let res = *all;
*all = dn;
Some(res)
}
None => {
*self = DirNames::Root(false);
Some("/")
}
}
}
DirNames::Root(true) => {
*self = DirNames::Root(false);
Some("/")
}
DirNames::Root(false) => None,
}
}
}
impl Path {
pub fn from_str(s: &str) -> Self {
if is_canonical(s) {
Path(ArcStr::from(s))
} else {
Path(ArcStr::from(canonize(s)))
}
}
pub fn root() -> Path {
Path::from("/")
}
pub fn is_absolute<T: AsRef<str> + ?Sized>(p: &T) -> bool {
p.as_ref().starts_with(SEP)
}
pub fn is_parent<T: AsRef<str> + ?Sized, U: AsRef<str> + ?Sized>(
parent: &T,
other: &U,
) -> bool {
let parent = parent.as_ref();
let other = other.as_ref();
parent == "/"
|| (other.starts_with(parent)
&& (other.len() == parent.len()
|| other.as_bytes()[parent.len()] == SEP as u8))
}
pub fn is_immediate_parent<T: AsRef<str> + ?Sized, U: AsRef<str> + ?Sized>(
parent: &T,
other: &U,
) -> bool {
let parent = if parent.as_ref() == "/" { None } else { Some(parent.as_ref()) };
other.as_ref().len() > 0
&& other.as_ref() != "/"
&& Path::dirname(other) == parent
}
pub fn strip_prefix<'a, T: AsRef<str> + ?Sized, U: AsRef<str> + ?Sized>(prefix: &T, path: &'a U) -> Option<&'a str> {
if Path::is_parent(prefix, path) {
path.as_ref().strip_prefix(prefix.as_ref()).map(|s| s.strip_prefix("/").unwrap_or(s))
} else {
None
}
}
pub fn lcp<'a, T: AsRef<str> + ?Sized, U: AsRef<str> + ?Sized>(
path0: &'a T,
path1: &'a U,
) -> &'a str {
let (mut p0, p1) = if path0.as_ref().len() <= path1.as_ref().len() {
(path0.as_ref(), path1.as_ref())
} else {
(path1.as_ref(), path0.as_ref())
};
loop {
if Path::is_parent(p0, p1) {
return p0;
} else {
match Path::dirname(p0) {
Some(p) => p0 = p,
None => return "/",
}
}
}
}
pub fn escape<T: AsRef<str> + ?Sized>(s: &T) -> Cow<str> {
utils::escape(s, ESC, &[SEP])
}
pub fn unescape<T: AsRef<str> + ?Sized>(s: &T) -> Cow<str> {
let s = s.as_ref();
if !(0..s.len()).into_iter().any(|i| utils::is_escaped(s, ESC, i)) {
Cow::Borrowed(s)
} else {
let mut out = String::with_capacity(s.len());
for (i, c) in s.chars().enumerate() {
if utils::is_escaped(s, ESC, i) {
out.pop();
}
out.push(c);
}
Cow::Owned(out)
}
}
pub fn append<T: AsRef<str> + ?Sized>(&self, other: &T) -> Self {
let other = other.as_ref();
if other.len() == 0 {
self.clone()
} else {
let mut res = String::with_capacity(self.as_ref().len() + other.len());
res.push_str(self.as_ref());
res.push(SEP);
res.push_str(other);
Path::from(res)
}
}
pub fn parts<T: AsRef<str> + ?Sized>(s: &T) -> impl Iterator<Item = &str> {
let s = s.as_ref();
let skip = if s == "/" {
2
} else if s.starts_with("/") {
1
} else {
0
};
utils::split_escaped(s, ESC, SEP).skip(skip)
}
pub fn dirnames<T: AsRef<str> + ?Sized>(s: &T) -> DirNames {
let s = s.as_ref();
if s == "/" {
DirNames::Root(true)
} else {
DirNames::Path { cur: s, all: s, base: 1 }
}
}
pub fn levels<T: AsRef<str> + ?Sized>(s: &T) -> usize {
let mut p = 0;
for _ in Path::parts(s) {
p += 1
}
p
}
pub fn dirname<'a, T: AsRef<str> + ?Sized>(s: &'a T) -> Option<&'a str> {
let s = s.as_ref();
Path::rfind_sep(s).and_then(|i| if i == 0 { None } else { Some(&s[0..i]) })
}
pub fn dirname_with_sep<T: AsRef<str> + ?Sized>(s: &T) -> Option<&str> {
let s = s.as_ref();
Path::rfind_sep(s).and_then(|i| if i == 0 { None } else { Some(&s[0..i + 1]) })
}
pub fn basename<T: AsRef<str> + ?Sized>(s: &T) -> Option<&str> {
let s = s.as_ref();
match Path::rfind_sep(s) {
None => {
if s.len() > 0 {
Some(s)
} else {
None
}
}
Some(i) => {
if s.len() <= 1 {
None
} else {
Some(&s[i + 1..s.len()])
}
}
}
}
fn find_sep_int<F: Fn(&str) -> Option<usize>>(mut s: &str, f: F) -> Option<usize> {
if s.len() == 0 {
None
} else {
loop {
match f(s) {
None => return None,
Some(i) => {
if !utils::is_escaped(s, ESC, i) {
return Some(i);
} else {
s = &s[0..i];
}
}
}
}
}
}
pub fn rfind_sep<T: AsRef<str> + ?Sized>(s: &T) -> Option<usize> {
let s = s.as_ref();
Path::find_sep_int(s, |s| s.rfind(SEP))
}
pub fn find_sep<T: AsRef<str> + ?Sized>(s: &T) -> Option<usize> {
let s = s.as_ref();
Path::find_sep_int(s, |s| s.find(SEP))
}
}