Skip to main content

path_name/
lib.rs

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