path-name 0.1.0

Fetching path names.
Documentation
//! Fetching path names.

#![deny(missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg))]

use std::{path::Path, str::Utf8Error};

#[cfg(feature = "diagnostics")]
use miette::Diagnostic;

use non_empty_str::{EmptyStr, NonEmptyStr, NonEmptyString};
use thiserror::Error;

mod sealed {
    pub trait Sealed {}
}

/// Represents errors that can occur when fetching path names.
#[derive(Debug, Error)]
#[cfg_attr(feature = "diagnostics", derive(Diagnostic))]
pub enum Error {
    /// Path name not found.
    #[error("path name not found")]
    #[cfg_attr(
        feature = "diagnostics",
        diagnostic(code(path_name::not_found), help("make sure the path name is present"))
    )]
    NotFound,

    /// Empty path name encountered.
    #[error("empty path name encountered")]
    #[cfg_attr(
        feature = "diagnostics",
        diagnostic(code(path_name::empty), help("make sure the path name is non-empty"))
    )]
    Empty(#[from] EmptyStr),

    /// Invalid UTF-8 in path name.
    #[error("invalid utf-8 in path name")]
    #[cfg_attr(
        feature = "diagnostics",
        diagnostic(code(path_name::utf8), help("make sure the path name is valid utf-8"))
    )]
    Utf8(#[from] Utf8Error),
}

/// Fetching path names.
pub trait PathName: sealed::Sealed {
    /// Tries to return the path name as non-empty string.
    ///
    /// # Errors
    ///
    /// Returns [`Error`] if the path name was not found, empty, or invalid UTF-8.
    fn path_name(&self) -> Result<&NonEmptyStr, Error>;

    /// Similar to [`path_name`], but returns non-empty owned strings.
    ///
    /// # Errors
    ///
    /// See [`path_name`].
    ///
    /// [`path_name`]: Self::path_name
    fn path_name_owned(&self) -> Result<NonEmptyString, Error> {
        self.path_name().map(ToOwned::to_owned)
    }
}

impl<P: AsRef<Path>> sealed::Sealed for P {}

impl<P: AsRef<Path>> PathName for P {
    fn path_name(&self) -> Result<&NonEmptyStr, Error> {
        let path = self.as_ref();

        let string: &str = path.file_name().ok_or(Error::NotFound)?.try_into()?;

        let name = string.try_into()?;

        Ok(name)
    }
}