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::hash::Hash;
use std::hash::Hasher;
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;
fn cow_path_into_base_path(path: Cow<'_, Path>) -> Cow<'_, BasePath> {
debug_assert!(imp::is_base(&path));
match path {
Cow::Borrowed(path) => {
Cow::Borrowed(BasePath::from_inner(path.as_os_str()))
}
Cow::Owned(path) => Cow::Owned(BasePathBuf(path)),
}
}
#[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[repr(transparent)]
pub struct BasePath(pub(super) OsStr);
impl BasePath {
pub(super) fn from_inner(path: &OsStr) -> &Self {
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)
})
}
#[inline]
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]
pub fn expand(&self) -> io::Result<Cow<'_, Self>> {
self.as_path().expand().map(cow_path_into_base_path)
}
#[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]
#[must_use]
pub fn is_symlink(&self) -> bool {
self.as_path().is_symlink()
}
#[inline]
pub fn join<P>(&self, path: P) -> BasePathBuf
where
P: AsRef<Path>,
{
let mut base = self.to_owned();
base.push(path);
base
}
#[cfg(feature = "localization")]
#[cfg_attr(normpath_docs_rs, doc(cfg(feature = "localization")))]
#[inline]
#[must_use]
pub fn localize_name(&self) -> Cow<'_, OsStr> {
self.as_path().localize_name()
}
#[inline]
pub fn metadata(&self) -> io::Result<Metadata> {
self.as_path().metadata()
}
#[inline]
pub fn normalize(&self) -> io::Result<BasePathBuf> {
self.as_path().normalize()
}
#[cfg(any(doc, windows))]
#[cfg_attr(normpath_docs_rs, doc(cfg(windows)))]
#[inline]
pub fn normalize_virtually(&self) -> io::Result<BasePathBuf> {
self.as_path().normalize_virtually()
}
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]
pub fn shorten(&self) -> io::Result<Cow<'_, Self>> {
self.as_path().shorten().map(cow_path_into_base_path)
}
#[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()
}
#[inline]
pub fn try_exists(&self) -> io::Result<bool> {
self.as_path().try_exists()
}
}
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_owned().into())
}
}
#[derive(Clone, Debug)]
pub struct BasePathBuf(pub(super) PathBuf);
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))
} else {
Err(MissingPrefixBufError(path))
}
}
#[inline]
#[must_use]
pub fn into_os_string(self) -> OsString {
self.0.into_os_string()
}
#[inline]
#[must_use]
pub fn into_path_buf(self) -> PathBuf {
self.0
}
#[inline]
pub fn pop(&mut self) -> Result<bool, ParentError> {
self.check_parent().map(|()| self.pop_unchecked())
}
#[inline]
pub fn pop_unchecked(&mut self) -> bool {
self.0.pop()
}
#[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.as_os_str()
}
}
impl AsRef<Path> for BasePathBuf {
#[inline]
fn as_ref(&self) -> &Path {
&self.0
}
}
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.as_os_str())
}
}
impl Eq for BasePathBuf {}
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.into_os_string()
}
}
impl From<BasePathBuf> for PathBuf {
#[inline]
fn from(value: BasePathBuf) -> Self {
value.0
}
}
impl Hash for BasePathBuf {
#[inline]
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
(**self).hash(state);
}
}
impl Ord for BasePathBuf {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
(**self).cmp(&**other)
}
}
impl PartialEq for BasePathBuf {
#[inline]
fn eq(&self, other: &Self) -> bool {
**self == **other
}
}
impl PartialOrd for BasePathBuf {
#[expect(clippy::non_canonical_partial_ord_impl)]
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
(**self).partial_cmp(&**other)
}
}
#[cfg(feature = "print_bytes")]
#[cfg_attr(normpath_docs_rs, doc(cfg(feature = "print_bytes")))]
mod print_bytes {
use print_bytes::ByteStr;
use print_bytes::ToBytes;
#[cfg(windows)]
use print_bytes::WideStr;
use super::BasePath;
use super::BasePathBuf;
impl ToBytes for BasePath {
#[inline]
fn to_bytes(&self) -> ByteStr<'_> {
self.0.to_bytes()
}
#[cfg(windows)]
#[inline]
fn to_wide(&self) -> Option<WideStr> {
self.0.to_wide()
}
}
impl ToBytes for BasePathBuf {
#[inline]
fn to_bytes(&self) -> ByteStr<'_> {
(**self).to_bytes()
}
#[cfg(windows)]
#[inline]
fn to_wide(&self) -> Option<WideStr> {
(**self).to_wide()
}
}
}
#[cfg(feature = "serde")]
#[cfg_attr(normpath_docs_rs, doc(cfg(feature = "serde")))]
mod serde {
use std::ffi::OsString;
use serde::Deserialize;
use serde::Deserializer;
use serde::Serialize;
use serde::Serializer;
use super::BasePath;
use super::BasePathBuf;
impl<'de> Deserialize<'de> for BasePathBuf {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
OsString::deserialize(deserializer).map(|x| Self(x.into()))
}
}
impl Serialize for BasePath {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_newtype_struct("BasePath", &self.0)
}
}
impl Serialize for BasePathBuf {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer
.serialize_newtype_struct("BasePathBuf", self.as_os_str())
}
}
}
#[cfg(feature = "uniquote")]
#[cfg_attr(normpath_docs_rs, doc(cfg(feature = "uniquote")))]
mod uniquote {
use uniquote::Formatter;
use uniquote::Quote;
use uniquote::Result;
use super::BasePath;
use super::BasePathBuf;
impl Quote for BasePath {
#[inline]
fn escape(&self, f: &mut Formatter<'_>) -> Result {
self.0.escape(f)
}
}
impl Quote for BasePathBuf {
#[inline]
fn escape(&self, f: &mut Formatter<'_>) -> Result {
(**self).escape(f)
}
}
}