diff --git a/crates/nu-path/src/form.rs b/crates/nu-path/src/form.rs index f61a9a0f11..3f0982a7fb 100644 --- a/crates/nu-path/src/form.rs +++ b/crates/nu-path/src/form.rs @@ -1,10 +1,16 @@ +use std::ffi::OsStr; + mod private { + use std::ffi::OsStr; + // This trait should not be extended by external crates in order to uphold safety guarantees. // As such, this trait is put inside a private module to prevent external impls. // This ensures that all possible [`PathForm`]s can only be defined here and will: // - be zero sized (enforced anyways by the `repr(transparent)` on `Path`) // - have a no-op [`Drop`] implementation - pub trait Sealed {} + pub trait Sealed { + fn invariants_satisfied + ?Sized>(path: &P) -> bool; + } } /// A marker trait for the different kinds of path forms. @@ -26,7 +32,11 @@ impl PathForm for Canonical {} /// trailing slashes, dot components (`..` or `.`), and repeated path separators. pub struct Any; -impl private::Sealed for Any {} +impl private::Sealed for Any { + fn invariants_satisfied + ?Sized>(_: &P) -> bool { + true + } +} /// A strictly relative path. /// @@ -34,7 +44,11 @@ impl private::Sealed for Any {} /// trailing slashes, dot components (`..` or `.`), and repeated path separators. pub struct Relative; -impl private::Sealed for Relative {} +impl private::Sealed for Relative { + fn invariants_satisfied + ?Sized>(path: &P) -> bool { + std::path::Path::new(path).is_relative() + } +} /// An absolute path. /// @@ -42,14 +56,22 @@ impl private::Sealed for Relative {} /// trailing slashes, dot components (`..` or `.`), and repeated path separators. pub struct Absolute; -impl private::Sealed for Absolute {} +impl private::Sealed for Absolute { + fn invariants_satisfied + ?Sized>(path: &P) -> bool { + std::path::Path::new(path).is_absolute() + } +} // A canonical path. // // An absolute path with all intermediate components normalized and symbolic links resolved. pub struct Canonical; -impl private::Sealed for Canonical {} +impl private::Sealed for Canonical { + fn invariants_satisfied + ?Sized>(_: &P) -> bool { + true + } +} /// A marker trait for [`PathForm`]s that may be relative paths. /// This includes only the [`Any`] and [`Relative`] path forms. diff --git a/crates/nu-path/src/path.rs b/crates/nu-path/src/path.rs index 3ebbf43a6f..f9d03425b5 100644 --- a/crates/nu-path/src/path.rs +++ b/crates/nu-path/src/path.rs @@ -161,6 +161,7 @@ impl Path
{ /// Create a new path of any form without validating invariants. #[inline] fn new_unchecked + ?Sized>(path: &P) -> &Self { + debug_assert!(Form::invariants_satisfied(path)); // Safety: `Path` is a repr(transparent) wrapper around `std::path::Path`. let path = std::path::Path::new(path.as_ref()); let ptr = std::ptr::from_ref(path) as *const Self; @@ -705,9 +706,11 @@ impl Path { /// ``` #[inline] pub fn try_absolute(&self) -> Result<&AbsolutePath, &RelativePath> { - self.is_absolute() - .then_some(AbsolutePath::new_unchecked(&self.inner)) - .ok_or(RelativePath::new_unchecked(&self.inner)) + if self.is_absolute() { + Ok(AbsolutePath::new_unchecked(&self.inner)) + } else { + Err(RelativePath::new_unchecked(&self.inner)) + } } /// Returns an `Ok` [`RelativePath`] if the [`Path`] is relative. @@ -722,9 +725,11 @@ impl Path { /// ``` #[inline] pub fn try_relative(&self) -> Result<&RelativePath, &AbsolutePath> { - self.is_relative() - .then_some(RelativePath::new_unchecked(&self.inner)) - .ok_or(AbsolutePath::new_unchecked(&self.inner)) + if self.is_relative() { + Ok(RelativePath::new_unchecked(&self.inner)) + } else { + Err(AbsolutePath::new_unchecked(&self.inner)) + } } } @@ -1372,6 +1377,7 @@ impl PathBuf { /// Create a new [`PathBuf`] of any form without validiting invariants. #[inline] pub(crate) fn new_unchecked(buf: std::path::PathBuf) -> Self { + debug_assert!(Form::invariants_satisfied(&buf)); Self { _form: PhantomData, inner: buf,