Skip to main content

soft_canonicalize/
error.rs

1use std::borrow::Cow;
2use std::path::{Path, PathBuf};
3use std::{fmt, io};
4
5/// Error payload used by this crate to attach the offending path to I/O errors.
6#[derive(Debug, Clone)]
7pub struct SoftCanonicalizeError {
8    path: PathBuf,
9    detail: Cow<'static, str>,
10}
11
12impl SoftCanonicalizeError {
13    pub fn new(path: PathBuf, detail: impl Into<Cow<'static, str>>) -> Self {
14        Self {
15            path,
16            detail: detail.into(),
17        }
18    }
19    /// Offending path that caused the error
20    pub fn path(&self) -> &Path {
21        &self.path
22    }
23    /// Human-readable error detail (without the path suffix)
24    pub fn detail(&self) -> &str {
25        &self.detail
26    }
27}
28
29impl fmt::Display for SoftCanonicalizeError {
30    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31        write!(f, "{} (path: '{}')", self.detail, self.path.display())
32    }
33}
34
35impl std::error::Error for SoftCanonicalizeError {}
36
37/// Convenience to create an io::Error with our path-aware payload.
38#[inline]
39pub(crate) fn error_with_path(
40    kind: io::ErrorKind,
41    path: &Path,
42    detail: impl Into<Cow<'static, str>>,
43) -> io::Error {
44    io::Error::new(kind, SoftCanonicalizeError::new(path.to_path_buf(), detail))
45}
46
47/// Extension to extract our path-aware payload from io::Error.
48pub trait IoErrorPathExt {
49    fn offending_path(&self) -> Option<&Path>;
50    fn soft_canon_detail(&self) -> Option<&str>;
51}
52
53impl IoErrorPathExt for io::Error {
54    fn offending_path(&self) -> Option<&Path> {
55        self.get_ref()
56            .and_then(|e| e.downcast_ref::<SoftCanonicalizeError>())
57            .map(|p| p.path())
58    }
59
60    fn soft_canon_detail(&self) -> Option<&str> {
61        self.get_ref()
62            .and_then(|e| e.downcast_ref::<SoftCanonicalizeError>())
63            .map(|p| p.detail())
64    }
65}