#[cfg(test)]
mod tests;
#[cfg(all(test, feature = "serde"))]
mod serde_tests;
use std::{
borrow::Borrow,
path::{Path, PathBuf},
};
macro_rules! impl_buf_traits {
($path_buf: ident, $serde_expected_type: literal) => {
impl AsRef<Path> for $path_buf {
fn as_ref(&self) -> &Path {
&self.path
}
}
impl std::str::FromStr for $path_buf {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::new(s).ok_or(concat!("Not ", $serde_expected_type))
}
}
#[cfg(feature = "serde")]
impl<'de> serde_core::Deserialize<'de> for $path_buf {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde_core::Deserializer<'de>,
{
let path_buf = PathBuf::deserialize(deserializer)?;
let path_buf = $path_buf { path: path_buf };
if path_buf.is_valid() {
Ok(path_buf)
} else {
Err(serde_core::de::Error::invalid_value(
serde_core::de::Unexpected::Str(&path_buf.to_string_lossy()),
&$serde_expected_type,
))
}
}
}
#[cfg(feature = "serde")]
impl serde_core::Serialize for $path_buf {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde_core::Serializer,
{
self.path.serialize(serializer)
}
}
};
}
macro_rules! impl_ref_path_traits {
($path_ref:ty, $serde_expected_type: literal) => {
impl std::ops::Deref for $path_ref {
type Target = Path;
fn deref(&self) -> &Self::Target {
&self.path
}
}
impl AsRef<Self> for $path_ref {
fn as_ref(&self) -> &Self {
self
}
}
impl AsRef<Path> for $path_ref {
fn as_ref(&self) -> &Path {
&self.path
}
}
#[cfg(feature = "serde")]
impl<'de: 'a, 'a> serde_core::Deserialize<'de> for &'a $path_ref {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde_core::Deserializer<'de>,
{
let path = <&'a Path>::deserialize(deserializer)?;
let path = wrap_ref_path!(path, $path_ref);
if path.is_valid() {
Ok(path)
} else {
Err(serde_core::de::Error::invalid_value(
serde_core::de::Unexpected::Str(&path.to_string_lossy()),
&$serde_expected_type,
))
}
}
}
#[cfg(feature = "serde")]
impl serde_core::Serialize for $path_ref {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde_core::Serializer,
{
self.path.serialize(serializer)
}
}
};
}
macro_rules! wrap_ref_path {
($path:expr, $path_ref:ty) => {{
let path: &Path = $path; let path: &<$path_ref as std::ops::Deref>::Target = path;
#[allow(unsafe_code)]
#[allow(clippy::as_conversions)]
#[allow(clippy::ref_as_ptr)]
unsafe {
&*(path as *const Path as *const $path_ref)
}
}};
}
macro_rules! impl_conv_traits {
($path_buf:ty, $path_ref:ty) => {
impl Borrow<$path_ref> for $path_buf {
fn borrow(&self) -> &$path_ref {
wrap_ref_path!(self.path.as_path(), $path_ref)
}
}
impl ToOwned for $path_ref {
type Owned = $path_buf;
fn to_owned(&self) -> Self::Owned {
Self::Owned {
path: self.path.to_path_buf(),
}
}
}
impl std::ops::Deref for $path_buf {
type Target = $path_ref;
fn deref(&self) -> &Self::Target {
self.borrow()
}
}
impl AsRef<$path_ref> for $path_buf {
fn as_ref(&self) -> &$path_ref {
self.borrow()
}
}
};
}
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct SingleComponentPathBuf {
pub(crate) path: PathBuf,
}
impl SingleComponentPathBuf {
#[inline]
pub fn new<S: Into<PathBuf>>(component: S) -> Option<Self> {
fn inner(component: PathBuf) -> Option<SingleComponentPathBuf> {
let component = SingleComponentPathBuf { path: component };
component.is_valid().then_some(component)
}
inner(component.into())
}
}
impl_buf_traits! {SingleComponentPathBuf, "a path with only a single forwarding component"}
#[derive(Debug, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[repr(transparent)]
pub struct SingleComponentPath {
pub(crate) path: Path,
}
impl SingleComponentPath {
#[inline]
pub fn new<P: AsRef<Path> + ?Sized>(component: &P) -> Option<&Self> {
fn inner(component: &Path) -> Option<&SingleComponentPath> {
let component = wrap_ref_path!(component, SingleComponentPath);
component.is_valid().then_some(component)
}
inner(component.as_ref())
}
pub(crate) fn is_valid(&self) -> bool {
use std::path::Component;
let mut components = self
.path
.components()
.filter(|component| !matches!(component, Component::CurDir));
matches!(
(components.next(), components.next()),
(Some(Component::Normal(_)), None)
)
}
}
impl_ref_path_traits! {SingleComponentPath, "a path with only a single forwarding component"}
impl_conv_traits! {SingleComponentPathBuf, SingleComponentPath}
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct MultiComponentPathBuf {
pub(crate) path: PathBuf,
}
impl MultiComponentPathBuf {
#[inline]
pub fn new<S: Into<PathBuf>>(component: S) -> Option<Self> {
fn inner(component: PathBuf) -> Option<MultiComponentPathBuf> {
let component = MultiComponentPathBuf { path: component };
component.is_valid().then_some(component)
}
inner(component.into())
}
}
impl_buf_traits! {MultiComponentPathBuf, "a relative, only forwarding, path"}
#[derive(Debug, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[repr(transparent)]
pub struct MultiComponentPath {
pub(crate) path: Path,
}
impl MultiComponentPath {
#[inline]
pub fn new<P: AsRef<Path> + ?Sized>(component: &P) -> Option<&Self> {
fn inner(component: &Path) -> Option<&MultiComponentPath> {
let component = wrap_ref_path!(component, MultiComponentPath);
component.is_valid().then_some(component)
}
inner(component.as_ref())
}
pub(crate) fn is_valid(&self) -> bool {
use std::path::Component;
self.path
.components()
.all(|component| matches!(component, Component::Normal(_) | Component::CurDir))
}
}
impl_ref_path_traits! {MultiComponentPath, "a relative, only forwarding, path"}
impl_conv_traits! {MultiComponentPathBuf, MultiComponentPath}
pub trait PushPathComponent {
fn push_component(&mut self, component: impl AsRef<SingleComponentPath>);
fn push_components(&mut self, component: impl AsRef<MultiComponentPath>);
}
impl PushPathComponent for PathBuf {
fn push_component(&mut self, component: impl AsRef<SingleComponentPath>) {
self.push(component.as_ref());
}
fn push_components(&mut self, component: impl AsRef<MultiComponentPath>) {
self.push(component.as_ref());
}
}
pub mod prelude {
pub use crate::PushPathComponent;
pub use crate::SingleComponentPath;
pub use crate::SingleComponentPathBuf;
pub use crate::MultiComponentPath;
pub use crate::MultiComponentPathBuf;
}