Add debug asserts

This commit is contained in:
Ian Manske 2024-06-22 11:04:53 -07:00
parent f0be6ddc08
commit 670f77be9b
2 changed files with 39 additions and 11 deletions

View File

@ -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<P: AsRef<OsStr> + ?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<P: AsRef<OsStr> + ?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<P: AsRef<OsStr> + ?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<P: AsRef<OsStr> + ?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<P: AsRef<OsStr> + ?Sized>(_: &P) -> bool {
true
}
}
/// A marker trait for [`PathForm`]s that may be relative paths.
/// This includes only the [`Any`] and [`Relative`] path forms.

View File

@ -161,6 +161,7 @@ impl<Form: PathForm> Path<Form> {
/// Create a new path of any form without validating invariants.
#[inline]
fn new_unchecked<P: AsRef<OsStr> + ?Sized>(path: &P) -> &Self {
debug_assert!(Form::invariants_satisfied(path));
// Safety: `Path<Form>` 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<Form: PathForm> PathBuf<Form> {
/// 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,