use core::{borrow::Borrow, cmp::Ordering, convert::Infallible, fmt::{self, Debug, Formatter}, hash::Hash, marker::PhantomData, ops::Deref};
use alloc::{borrow::{Cow, ToOwned}, boxed::Box, string::{String, ToString}};
use itertools::{EitherOrBoth, Itertools};
use thiserror::Error;
use crate::{env::current_dir, ffi::{OsStr, OsString, StrDisplay}, fs::{self, DirEntry, Metadata}, io::Error};
#[derive(Clone, Default)]
pub struct PathBuf {
data: OsString
}
impl PathBuf {
pub const fn new() -> Self {
Self {
data: OsString::new()
}
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
data: OsString::with_capacity(capacity)
}
}
pub fn as_path(&self) -> &Path {
self.as_ref()
}
pub fn push(&mut self, path: impl AsRef<Path>) {
let path = path.as_ref();
if path.is_absolute() {
*self = path.to_owned();
} else {
if !self.data.ends_with("/") {
self.data.push("/");
}
self.data.push(&path.data);
}
}
pub fn pop(&mut self) -> bool {
if let Some(parent) = self.parent() {
*self = parent.to_owned();
true
} else {
false
}
}
pub fn set_file_name(&mut self, name: impl AsRef<OsStr>) {
if let Some(_) = self.file_name() {
self.pop();
}
self.push(name.as_ref());
}
pub fn set_extension(&mut self, extension: impl AsRef<OsStr>) -> bool {
if let Some(name) = self.file_name() {
let extension = extension.as_ref();
let index = if name.starts_with(".") {
name.match_indices(".").skip(1).last()
} else {
name.match_indices(".").last()
}.map(|(index, _)| index);
if let Some(index) = index {
let mut new_name = OsStr::new(&name.as_str()[..index]).to_owned();
new_name.push(".");
new_name.push(extension);
self.set_file_name(new_name);
true
} else if extension.is_empty() {
true
} else {
let mut new_name = name.to_owned();
new_name.push(".");
new_name.push(extension);
self.set_file_name(new_name);
true
}
} else {
false
}
}
pub fn add_extension(&mut self, extension: impl AsRef<OsStr>) -> bool {
if let Some(name) = self.file_name() {
let extension = extension.as_ref();
if !extension.is_empty() {
let mut name = name.to_owned();
name.push(".");
name.push(extension);
self.set_file_name(name);
}
true
} else {
false
}
}
pub fn into_os_string(self) -> OsString {
self.data
}
pub fn capacity(&self) -> usize {
self.data.capacity()
}
pub fn clear(&mut self) {
self.data.clear();
}
}
impl Debug for PathBuf {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Debug::fmt(self.data.as_os_string(), f)
}
}
impl PartialEq for PathBuf {
fn eq(&self, other: &Self) -> bool {
self.as_path() == other.as_path()
}
}
impl Eq for PathBuf {
}
impl PartialOrd for PathBuf {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.as_path().partial_cmp(other.as_path())
}
}
impl Ord for PathBuf {
fn cmp(&self, other: &Self) -> Ordering {
self.as_path().cmp(other.as_path())
}
}
impl Hash for PathBuf {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.as_path().hash(state);
}
}
impl Deref for PathBuf {
type Target = Path;
fn deref(&self) -> &Self::Target {
Path::new(&self.data)
}
}
impl Borrow<Path> for PathBuf {
fn borrow(&self) -> &Path {
Path::new(self.data.as_str())
}
}
impl AsRef<Path> for PathBuf {
fn as_ref(&self) -> &Path {
Path::new(&self.data)
}
}
impl From<OsString> for PathBuf {
fn from(value: OsString) -> Self {
Self {
data: value
}
}
}
pub struct Path {
data: OsStr
}
impl AsRef<Path> for Path {
fn as_ref(&self) -> &Path {
self
}
}
impl AsRef<Path> for OsStr {
fn as_ref(&self) -> &Path {
Path::new(self)
}
}
impl AsRef<Path> for OsString {
fn as_ref(&self) -> &Path {
Path::new(self)
}
}
impl AsRef<Path> for str {
fn as_ref(&self) -> &Path {
Path::new(self)
}
}
impl AsRef<Path> for String {
fn as_ref(&self) -> &Path {
Path::new(self)
}
}
impl Debug for Path {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Debug::fmt(&self.data, f)
}
}
impl PartialEq for Path {
fn eq(&self, other: &Self) -> bool {
self.components().zip_longest(other.components())
.all(|value| match value {
EitherOrBoth::Both(a, b) => a == b,
_ => false,
})
}
}
impl PartialEq<OsString> for Path {
fn eq(&self, other: &OsString) -> bool {
self == Path::new(other)
}
}
impl PartialEq<OsString> for &Path {
fn eq(&self, other: &OsString) -> bool {
self == &Path::new(other)
}
}
impl PartialEq<OsStr> for Path {
fn eq(&self, other: &OsStr) -> bool {
self == Path::new(other)
}
}
impl PartialEq<OsStr> for &Path {
fn eq(&self, other: &OsStr) -> bool {
self == &Path::new(other)
}
}
impl PartialEq<&OsStr> for Path {
fn eq(&self, other: &&OsStr) -> bool {
self.eq(Path::new(other))
}
}
impl PartialEq<Path> for OsStr {
fn eq(&self, other: &Path) -> bool {
Path::new(self) == other
}
}
impl PartialEq<&Path> for OsStr {
fn eq(&self, other: &&Path) -> bool {
Path::new(self) == *other
}
}
impl PartialEq<Path> for OsString {
fn eq(&self, other: &Path) -> bool {
Path::new(self) == other
}
}
impl PartialEq<&Path> for OsString {
fn eq(&self, other: &&Path) -> bool {
Path::new(self) == *other
}
}
impl PartialEq<&Path> for PathBuf {
fn eq(&self, other: &&Path) -> bool {
self.as_path() == *other
}
}
impl PartialEq<Path> for PathBuf {
fn eq(&self, other: &Path) -> bool {
self.as_path() == other
}
}
impl <P: AsRef<Path>> Extend<P> for PathBuf {
fn extend<T: IntoIterator<Item = P>>(&mut self, iter: T) {
for component in iter {
self.push(component);
}
}
}
impl PartialEq<PathBuf> for Path {
fn eq(&self, other: &PathBuf) -> bool {
self == other.as_path()
}
}
impl PartialEq<PathBuf> for &Path {
fn eq(&self, other: &PathBuf) -> bool {
*self == other.as_path()
}
}
impl PartialEq<String> for Path {
fn eq(&self, other: &String) -> bool {
self == Path::new(other)
}
}
impl PartialEq<String> for &Path {
fn eq(&self, other: &String) -> bool {
self == &Path::new(other)
}
}
impl PartialEq<str> for Path {
fn eq(&self, other: &str) -> bool {
self == Path::new(other)
}
}
impl PartialEq<str> for &Path {
fn eq(&self, other: &str) -> bool {
self == &Path::new(other)
}
}
impl PartialEq<&str> for Path {
fn eq(&self, other: &&str) -> bool {
self.eq(Path::new(other))
}
}
impl PartialEq<Path> for str {
fn eq(&self, other: &Path) -> bool {
Path::new(self) == other
}
}
impl PartialEq<&Path> for str {
fn eq(&self, other: &&Path) -> bool {
Path::new(self) == *other
}
}
impl PartialEq<Path> for String {
fn eq(&self, other: &Path) -> bool {
Path::new(self) == other
}
}
impl PartialEq<&Path> for String {
fn eq(&self, other: &&Path) -> bool {
Path::new(self) == *other
}
}
impl Eq for Path {
}
impl PartialOrd for Path {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl PartialOrd<&OsStr> for Path {
fn partial_cmp(&self, other: &&OsStr) -> Option<Ordering> {
self.partial_cmp(Path::new(other))
}
}
impl PartialOrd<OsStr> for &Path {
fn partial_cmp(&self, other: &OsStr) -> Option<Ordering> {
self.partial_cmp(&Path::new(other))
}
}
impl PartialOrd<OsStr> for Path {
fn partial_cmp(&self, other: &OsStr) -> Option<Ordering> {
self.partial_cmp(Path::new(other))
}
}
impl PartialOrd<&Path> for OsStr {
fn partial_cmp(&self, other: &&Path) -> Option<Ordering> {
Path::new(self).partial_cmp(*other)
}
}
impl PartialOrd<Path> for OsStr {
fn partial_cmp(&self, other: &Path) -> Option<Ordering> {
Path::new(self).partial_cmp(other)
}
}
impl PartialOrd<OsString> for &Path {
fn partial_cmp(&self, other: &OsString) -> Option<Ordering> {
self.partial_cmp(&Path::new(other))
}
}
impl PartialOrd<OsString> for Path {
fn partial_cmp(&self, other: &OsString) -> Option<Ordering> {
self.partial_cmp(Path::new(other))
}
}
impl PartialOrd<&Path> for OsString {
fn partial_cmp(&self, other: &&Path) -> Option<Ordering> {
Path::new(self).partial_cmp(*other)
}
}
impl PartialOrd<Path> for OsString {
fn partial_cmp(&self, other: &Path) -> Option<Ordering> {
Path::new(self).partial_cmp(other)
}
}
impl PartialOrd<&Path> for PathBuf {
fn partial_cmp(&self, other: &&Path) -> Option<Ordering> {
self.as_path().partial_cmp(*other)
}
}
impl PartialOrd<Path> for PathBuf {
fn partial_cmp(&self, other: &Path) -> Option<Ordering> {
self.as_path().partial_cmp(other)
}
}
impl PartialOrd<PathBuf> for Path {
fn partial_cmp(&self, other: &PathBuf) -> Option<Ordering> {
self.partial_cmp(other.as_path())
}
}
impl Ord for Path {
fn cmp(&self, other: &Self) -> Ordering {
for value in self.components().zip_longest(other.components()) {
match value {
EitherOrBoth::Both(a, b) => match a.cmp(&b) {
Ordering::Less => return Ordering::Less,
Ordering::Equal => (),
Ordering::Greater => return Ordering::Greater,
},
EitherOrBoth::Left(_) => return Ordering::Less,
EitherOrBoth::Right(_) => return Ordering::Greater,
}
}
Ordering::Equal
}
}
impl Hash for Path {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
for value in self.components() {
value.hash(state);
}
}
}
impl ToOwned for Path {
type Owned = PathBuf;
fn to_owned(&self) -> Self::Owned {
Self::Owned {
data: self.data.to_owned(),
}
}
}
impl Path {
pub fn new<S: AsRef<OsStr> + ?Sized>(path: &S) -> &Self {
unsafe {&*(path.as_ref() as *const OsStr as *const Path)}
}
pub fn as_os_str(&self) -> &OsStr {
unsafe {&*(self as *const Path as *const OsStr)}
}
pub fn to_str(&self) -> Option<&str> {
Some(self.data.as_str())
}
pub(crate) fn as_str(&self) -> &str {
self.data.as_str()
}
pub fn to_string_lossy(&self) -> Cow<'_, str> {
self.data.to_string_lossy()
}
pub fn to_path_buf(&self) -> PathBuf {
PathBuf {
data: self.data.to_owned()
}
}
pub fn is_absolute(&self) -> bool {
self.has_root()
}
pub fn is_relative(&self) -> bool {
!self.is_absolute()
}
pub fn has_root(&self) -> bool {
self.data.starts_with("/")
}
pub fn parent(&self) -> Option<&Self> {
if &self.data == "/" {
None
} else {
let path = self.without_trailing_slash();
path.data.rmatch_indices("/")
.map(|(i, _)| Path::new(&self.data.as_str()[0..(i + 1)]))
.next()
}
}
pub fn ancestors(&self) -> impl Iterator<Item = &Self> {
if &self.data == "/" {
Box::new(Some(self).into_iter()) as Box<dyn Iterator<Item = &Self>>
} else {
let path = self.without_trailing_slash();
Box::new(Some(self).into_iter().chain(
path.data.rmatch_indices("/")
.map(|(i, _)| Path::new(&self.data.as_str()[0..(i + 1)]))
)) as Box<dyn Iterator<Item = &Self>>
}
}
pub fn file_name(&self) -> Option<&OsStr> {
self.components().filter(|x| x != &Component::CurDir).last().and_then(|component| match component {
Component::RootDir => None,
Component::Normal(name) => Some(name),
Component::CurDir => None,
Component::ParentDir => None,
Component::Prefix(_) => unreachable!("prefix path component")
})
}
pub fn strip_prefix(&self, base: impl AsRef<Path>) -> Result<&Self, StripPrefixError> {
let prefix = base.as_ref().with_trailing_slash();
if self.data.starts_with(prefix.data.as_str()) {
Ok(Path::new(&self.data.as_str()[prefix.data.len()..]))
} else {
Err(StripPrefixError)
}
}
pub fn ends_with(&self, child: impl AsRef<Path>) -> bool {
let child = child.as_ref();
self.without_prefixes().any(|path| path == child)
}
pub fn starts_with(&self, parent: impl AsRef<Path>) -> bool {
let parent = parent.as_ref();
self.ancestors().any(|path| path == parent)
}
pub fn file_stem(&self) -> Option<&OsStr> {
let name = self.file_name()?;
let index = if name.starts_with(".") {
name.match_indices(".").skip(1).last()
} else {
name.match_indices(".").last()
}.map(|(index, _)| index);
if let Some(index) = index {
Some(OsStr::new(&name.as_str()[..index]))
} else {
Some(name)
}
}
pub fn file_prefix(&self) -> Option<&OsStr> {
let name = self.file_name()?;
let index = if name.starts_with(".") {
name.match_indices(".").skip(1).next()
} else {
name.match_indices(".").next()
}.map(|(index, _)| index);
if let Some(index) = index {
Some(OsStr::new(&name.as_str()[..index]))
} else {
Some(name)
}
}
pub fn extension(&self) -> Option<&OsStr> {
let name = self.file_name()?;
let index = if name.starts_with(".") {
name.match_indices(".").skip(1).last()
} else {
name.match_indices(".").last()
}.map(|(index, _)| index);
if let Some(index) = index {
Some(OsStr::new(&name.as_str()[index + 1..]))
} else {
None
}
}
pub fn join(&self, path: impl AsRef<Self>) -> PathBuf {
let mut buffer = self.to_owned();
buffer.push(path);
buffer
}
pub fn with_file_name(&self, name: impl AsRef<OsStr>) -> PathBuf {
let mut buffer = self.to_owned();
buffer.set_file_name(name);
buffer
}
pub fn with_extension(&self, name: impl AsRef<OsStr>) -> PathBuf {
let mut buffer = self.to_owned();
buffer.set_extension(name);
buffer
}
pub fn with_added_extension(&self, name: impl AsRef<OsStr>) -> PathBuf {
let mut buffer = self.to_owned();
buffer.add_extension(name);
buffer
}
pub fn components(&self) -> impl Iterator<Item = Component<'_>> {
if &self.data == "/" {
Box::new(Some(Component::RootDir).into_iter()) as Box<dyn Iterator<Item = Component<'_>>>
} else if self.has_root() {
Box::new(Some(Component::RootDir).into_iter()
.chain(Path::new(&self.data.as_str()[1..]).components())) as Box<dyn Iterator<Item = Component<'_>>>
} else {
Box::new(self.data.as_str().split("/").filter(|s| !s.is_empty()).map(|component| match component {
"." => Component::CurDir,
".." => Component::ParentDir,
_ => Component::Normal(component.as_ref())
})) as Box<dyn Iterator<Item = Component<'_>>>
}
}
pub fn iter(&self) -> impl Iterator<Item = &OsStr> {
if &self.data == "/" {
Box::new(Some(OsStr::new("/")).into_iter()) as Box<dyn Iterator<Item = &OsStr>>
} else if self.has_root() {
Box::new(Some(OsStr::new("/")).into_iter()
.chain(Path::new(&self.data.as_str()[1..]).iter())) as Box<dyn Iterator<Item = &OsStr>>
} else {
Box::new(self.data.as_str().split("/").map(OsStr::new)) as Box<dyn Iterator<Item = &OsStr>>
}
}
pub fn display(&self) -> StrDisplay<'_> {
self.data.display()
}
pub fn metadata(&self) -> Result<Metadata, Error> {
fs::metadata(self)
}
pub fn symlink_metadata(&self) -> Result<Metadata, Error> {
fs::symlink_metadata(self)
}
pub fn canonicalize(&self) -> Result<PathBuf, Error> {
fs::canonicalize(self)
}
pub fn absolute(&self) -> Result<PathBuf, Error> {
if self.is_absolute() {
Ok(self.to_owned())
} else {
let mut current_dir = current_dir()?;
current_dir.push(self);
Ok(current_dir)
}
}
pub fn read_link(&self) -> Result<PathBuf, Error> {
fs::read_link(self)
}
pub fn read_dir(&self) -> Result<impl Iterator<Item = Result<DirEntry, Error>>, Error> {
fs::read_dir(self)
}
pub fn exists(&self) -> bool {
fs::exists(self).unwrap_or(false)
}
pub fn try_exists(&self) -> Result<bool, Error> {
fs::exists(self)
}
pub fn is_file(&self) -> bool {
todo!()
}
pub fn is_dir(&self) -> bool {
todo!()
}
pub fn is_symlink(&self) -> bool {
todo!()
}
fn without_trailing_slash(&self) -> &Self {
if self.data.ends_with("/") {
Path::new(&self.data.as_str()[0..self.data.len()])
} else {
&self
}
}
fn with_trailing_slash(&self) -> PathBuf {
if !self.data.ends_with("/") {
PathBuf::from(OsString::from(self.data.to_str().unwrap().to_string() + "/"))
} else {
self.to_path_buf()
}
}
fn without_prefixes(&self) -> impl Iterator<Item = &Self> {
if &self.data == "/" {
Box::new(Some(self).into_iter()) as Box<dyn Iterator<Item = &Self>>
} else {
let path = self.without_trailing_slash();
Box::new(Some(self).into_iter().chain(
path.data.match_indices("/")
.map(|(i, _)| Path::new(&self.data.as_str()[(i + 1)..]))
)) as Box<dyn Iterator<Item = &Self>>
}
}
}
impl AsRef<OsStr> for Path {
fn as_ref(&self) -> &OsStr {
&self.data
}
}
impl AsRef<OsStr> for PathBuf {
fn as_ref(&self) -> &OsStr {
self.as_os_str()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Component<'a> {
Prefix(PrefixComponent<'a>),
RootDir,
CurDir,
ParentDir,
Normal(&'a OsStr)
}
impl AsRef<OsStr> for Component<'_> {
fn as_ref(&self) -> &OsStr {
match self {
Component::Prefix(_) => unimplemented!(),
Component::RootDir => OsStr::new("/"),
Component::CurDir => OsStr::new("."),
Component::ParentDir => OsStr::new(".."),
Component::Normal(os_str) => *os_str,
}
}
}
impl AsRef<Path> for Component<'_> {
fn as_ref(&self) -> &Path {
let value: &OsStr = self.as_ref();
Path::new(value)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PrefixComponent<'a> {
no: Infallible,
marker: PhantomData<&'a str>
}
#[derive(Debug, Error, PartialEq, Eq)]
#[error("failed to strip prefix")]
pub struct StripPrefixError;