path_name/
lib.rs

1//! Fetching path names.
2
3#![deny(missing_docs)]
4#![cfg_attr(docsrs, feature(doc_cfg))]
5
6use std::{path::Path, str::Utf8Error};
7
8#[cfg(feature = "diagnostics")]
9use miette::Diagnostic;
10
11use non_empty_str::{EmptyStr, NonEmptyStr, NonEmptyString};
12use thiserror::Error;
13
14mod sealed {
15    pub trait Sealed {}
16}
17
18/// Represents errors that can occur when fetching path names.
19#[derive(Debug, Error)]
20#[cfg_attr(feature = "diagnostics", derive(Diagnostic))]
21pub enum Error {
22    /// Path name not found.
23    #[error("path name not found")]
24    #[cfg_attr(
25        feature = "diagnostics",
26        diagnostic(code(path_name::not_found), help("make sure the path name is present"))
27    )]
28    NotFound,
29
30    /// Empty path name encountered.
31    #[error("empty path name encountered")]
32    #[cfg_attr(
33        feature = "diagnostics",
34        diagnostic(code(path_name::empty), help("make sure the path name is non-empty"))
35    )]
36    Empty(#[from] EmptyStr),
37
38    /// Invalid UTF-8 in path name.
39    #[error("invalid utf-8 in path name")]
40    #[cfg_attr(
41        feature = "diagnostics",
42        diagnostic(code(path_name::utf8), help("make sure the path name is valid utf-8"))
43    )]
44    Utf8(#[from] Utf8Error),
45}
46
47/// Fetching path names.
48pub trait PathName: sealed::Sealed {
49    /// Tries to return the path name as non-empty string.
50    ///
51    /// # Errors
52    ///
53    /// Returns [`Error`] if the path name was not found, empty, or invalid UTF-8.
54    fn path_name(&self) -> Result<&NonEmptyStr, Error>;
55
56    /// Similar to [`path_name`], but returns non-empty owned strings.
57    ///
58    /// # Errors
59    ///
60    /// See [`path_name`].
61    ///
62    /// [`path_name`]: Self::path_name
63    fn path_name_owned(&self) -> Result<NonEmptyString, Error> {
64        self.path_name().map(ToOwned::to_owned)
65    }
66}
67
68impl<P: AsRef<Path>> sealed::Sealed for P {}
69
70impl<P: AsRef<Path>> PathName for P {
71    fn path_name(&self) -> Result<&NonEmptyStr, Error> {
72        let path = self.as_ref();
73
74        let string: &str = path.file_name().ok_or(Error::NotFound)?.try_into()?;
75
76        let name = string.try_into()?;
77
78        Ok(name)
79    }
80}