use std::borrow::Borrow;
use std::borrow::Cow;
use std::cmp::Ordering;
use std::ffi::OsStr;
use std::ffi::OsString;
use std::fs::Metadata;
use std::fs::ReadDir;
use std::io;
use std::mem;
use std::ops::Deref;
use std::path::Component;
use std::path::Components;
use std::path::Path;
use std::path::PathBuf;
use super::error::MissingPrefixBufError;
use super::error::MissingPrefixError;
use super::error::ParentError;
use super::imp;
use super::PathExt;
#[derive(Debug, Eq, Hash, PartialEq, PartialOrd)]
#[repr(transparent)]
pub struct BasePath(pub(super) OsStr);
impl BasePath {
fn from_inner(path: &OsStr) -> &Self {
#[allow(clippy::transmute_ptr_to_ptr)]
unsafe {
mem::transmute(path)
}
}
#[inline]
pub fn new<'a, P>(path: P) -> io::Result<Cow<'a, Self>>
where
P: Into<Cow<'a, Path>>,
{
let path = path.into();
match path {
Cow::Borrowed(path) => Self::try_new(path)
.map(Cow::Borrowed)
.or_else(|_| imp::to_base(path).map(Cow::Owned)),
Cow::Owned(path) => BasePathBuf::new(path).map(Cow::Owned),
}
}
#[inline]
pub fn try_new<P>(path: &P) -> Result<&Self, MissingPrefixError>
where
P: AsRef<Path> + ?Sized,
{
let path = path.as_ref();
if imp::is_base(path) {
Ok(Self::from_inner(path.as_os_str()))
} else {
Err(MissingPrefixError(()))
}
}
#[inline]
#[must_use]
pub fn as_os_str(&self) -> &OsStr {
&self.0
}
#[inline]
#[must_use]
pub fn as_path(&self) -> &Path {
Path::new(&self.0)
}
#[inline]
pub fn canonicalize(&self) -> io::Result<BasePathBuf> {
self.as_path().canonicalize().map(|base| {
debug_assert!(imp::is_base(&base));
BasePathBuf(base.into_os_string())
})
}
#[inline]
#[must_use]
pub fn components(&self) -> Components<'_> {
self.as_path().components()
}
#[inline]
#[must_use]
pub fn ends_with<P>(&self, child: P) -> bool
where
P: AsRef<Path>,
{
self.as_path().ends_with(child)
}
#[inline]
#[must_use]
pub fn exists(&self) -> bool {
self.as_path().exists()
}
#[inline]
#[must_use]
pub fn extension(&self) -> Option<&OsStr> {
self.as_path().extension()
}
#[inline]
#[must_use]
pub fn file_name(&self) -> Option<&OsStr> {
self.as_path().file_name()
}
#[inline]
#[must_use]
pub fn file_stem(&self) -> Option<&OsStr> {
self.as_path().file_stem()
}
#[inline]
#[must_use]
pub fn has_root(&self) -> bool {
self.as_path().has_root()
}
#[inline]
#[must_use]
pub fn is_absolute(&self) -> bool {
self.as_path().is_absolute()
}
#[inline]
#[must_use]
pub fn is_dir(&self) -> bool {
self.as_path().is_dir()
}
#[inline]
#[must_use]
pub fn is_file(&self) -> bool {
self.as_path().is_file()
}
#[inline]
#[must_use]
pub fn is_relative(&self) -> bool {
self.as_path().is_relative()
}
#[inline]
pub fn join<P>(&self, path: P) -> BasePathBuf
where
P: AsRef<Path>,
{
let mut base = self.to_owned();
base.push(path);
base
}
#[inline]
pub fn metadata(&self) -> io::Result<Metadata> {
self.as_path().metadata()
}
#[inline]
pub fn normalize(&self) -> io::Result<BasePathBuf> {
self.as_path().normalize()
}
fn check_parent(&self) -> Result<(), ParentError> {
self.components()
.next_back()
.filter(|x| matches!(x, Component::Normal(_) | Component::RootDir))
.map(|_| ())
.ok_or(ParentError(()))
}
#[inline]
pub fn parent(&self) -> Result<Option<&Self>, ParentError> {
self.check_parent().map(|()| self.parent_unchecked())
}
#[inline]
#[must_use]
pub fn parent_unchecked(&self) -> Option<&Self> {
self.as_path()
.parent()
.map(|x| Self::from_inner(x.as_os_str()))
}
#[inline]
pub fn read_dir(&self) -> io::Result<ReadDir> {
self.as_path().read_dir()
}
#[inline]
pub fn read_link(&self) -> io::Result<PathBuf> {
self.as_path().read_link()
}
#[inline]
#[must_use]
pub fn starts_with<P>(&self, base: P) -> bool
where
P: AsRef<Path>,
{
self.as_path().starts_with(base)
}
#[inline]
pub fn symlink_metadata(&self) -> io::Result<Metadata> {
self.as_path().symlink_metadata()
}
}
impl AsRef<OsStr> for BasePath {
#[inline]
fn as_ref(&self) -> &OsStr {
&self.0
}
}
impl AsRef<Path> for BasePath {
#[inline]
fn as_ref(&self) -> &Path {
self.as_path()
}
}
impl AsRef<Self> for BasePath {
#[inline]
fn as_ref(&self) -> &Self {
self
}
}
impl<'a> From<&'a BasePath> for Cow<'a, BasePath> {
#[inline]
fn from(value: &'a BasePath) -> Self {
Cow::Borrowed(value)
}
}
impl PartialEq<Path> for BasePath {
#[inline]
fn eq(&self, other: &Path) -> bool {
&self.0 == other.as_os_str()
}
}
impl PartialEq<BasePath> for Path {
#[inline]
fn eq(&self, other: &BasePath) -> bool {
other == self
}
}
impl PartialOrd<Path> for BasePath {
#[inline]
fn partial_cmp(&self, other: &Path) -> Option<Ordering> {
self.0.partial_cmp(other.as_os_str())
}
}
impl PartialOrd<BasePath> for Path {
#[inline]
fn partial_cmp(&self, other: &BasePath) -> Option<Ordering> {
other.partial_cmp(self)
}
}
impl ToOwned for BasePath {
type Owned = BasePathBuf;
#[inline]
fn to_owned(&self) -> Self::Owned {
BasePathBuf(self.0.to_os_string())
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct BasePathBuf(pub(super) OsString);
impl BasePathBuf {
#[inline]
pub fn new<P>(path: P) -> io::Result<Self>
where
P: Into<PathBuf>,
{
Self::try_new(path).or_else(|x| imp::to_base(&x.0))
}
#[inline]
pub fn try_new<P>(path: P) -> Result<Self, MissingPrefixBufError>
where
P: Into<PathBuf>,
{
let path = path.into();
if imp::is_base(&path) {
Ok(Self(path.into_os_string()))
} else {
Err(MissingPrefixBufError(path))
}
}
#[inline]
#[must_use]
pub fn into_os_string(self) -> OsString {
self.0
}
#[inline]
#[must_use]
pub fn into_path_buf(self) -> PathBuf {
self.0.into()
}
#[inline]
pub fn pop(&mut self) -> Result<bool, ParentError> {
self.check_parent().map(|()| self.pop_unchecked())
}
pub(super) fn replace_with<F>(&mut self, replace_fn: F)
where
F: FnOnce(PathBuf) -> PathBuf,
{
self.0 = replace_fn(mem::take(&mut self.0).into()).into_os_string();
}
#[inline]
pub fn pop_unchecked(&mut self) -> bool {
let mut result = false;
self.replace_with(|mut base| {
result = base.pop();
base
});
result
}
#[inline]
pub fn push<P>(&mut self, path: P)
where
P: AsRef<Path>,
{
imp::push(self, path.as_ref())
}
}
impl AsRef<OsStr> for BasePathBuf {
#[inline]
fn as_ref(&self) -> &OsStr {
&self.0
}
}
impl AsRef<Path> for BasePathBuf {
#[inline]
fn as_ref(&self) -> &Path {
self.as_path()
}
}
impl AsRef<BasePath> for BasePathBuf {
#[inline]
fn as_ref(&self) -> &BasePath {
self
}
}
impl Borrow<BasePath> for BasePathBuf {
#[inline]
fn borrow(&self) -> &BasePath {
self
}
}
impl Deref for BasePathBuf {
type Target = BasePath;
#[inline]
fn deref(&self) -> &BasePath {
BasePath::from_inner(&self.0)
}
}
impl From<BasePathBuf> for Cow<'_, BasePath> {
#[inline]
fn from(value: BasePathBuf) -> Self {
Cow::Owned(value)
}
}
impl From<BasePathBuf> for OsString {
#[inline]
fn from(value: BasePathBuf) -> Self {
value.0
}
}
impl From<BasePathBuf> for PathBuf {
#[inline]
fn from(value: BasePathBuf) -> Self {
value.into_path_buf()
}
}