dwm-status 1.8.1

Displays system information for dwm status bar.
Documentation
use std::fmt;
pub(crate) use std::result::Result as StdResult;

use log::error;

pub(crate) type Result<T> = StdResult<T, Error>;

#[derive(Debug)]
#[cfg_attr(test, derive(Clone, PartialEq))]
pub struct Error {
    name: String,
    description: String,
    cause: Option<String>,
}

impl Error {
    fn new<N, D, E>(name: N, description: D, cause: E) -> Self
    where
        N: Into<String>,
        D: Into<String>,
        E: fmt::Debug,
    {
        Self {
            name: name.into(),
            description: description.into(),
            cause: Some(format!("{:?}", cause)),
        }
    }

    #[cfg(test)]
    pub(crate) fn new_test<N, D, E>(name: N, description: D, cause: E) -> Self
    where
        N: Into<String>,
        D: Into<String>,
        E: fmt::Debug,
    {
        Self::new(name, description, cause)
    }

    pub(crate) fn new_custom<N, D>(name: N, description: D) -> Self
    where
        N: Into<String>,
        D: Into<String>,
    {
        Self {
            name: name.into(),
            description: description.into(),
            cause: None,
        }
    }

    pub fn show_error(self) {
        error!("{}", self);
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "Error in {}: {}", self.name, self.description)?;

        if let Some(ref cause) = self.cause {
            write!(f, " ({})", cause)?;
        }

        Ok(())
    }
}

pub(crate) trait WrapErrorExt<T> {
    fn wrap_error<N, D>(self, name: N, description: D) -> Result<T>
    where
        N: Into<String>,
        D: Into<String>;
}

impl<T, E: fmt::Debug> WrapErrorExt<T> for StdResult<T, E> {
    fn wrap_error<N, D>(self, name: N, description: D) -> Result<T>
    where
        N: Into<String>,
        D: Into<String>,
    {
        self.map_err(|error| Error::new(name, description, error))
    }
}

impl<T> WrapErrorExt<T> for Option<T> {
    fn wrap_error<N, D>(self, name: N, description: D) -> Result<T>
    where
        N: Into<String>,
        D: Into<String>,
    {
        self.ok_or_else(|| Error::new_custom(name, description))
    }
}

pub(crate) trait ResultExt<T> {
    fn show_error_and_ignore(self);
}

impl<T> ResultExt<T> for Result<T> {
    fn show_error_and_ignore(self) {
        let _result = self.map_err(Error::show_error);
    }
}

#[cfg(test)]
mod tests {
    use hamcrest2::assert_that;
    use hamcrest2::prelude::*;

    use crate::test_utils::log::Level;
    use crate::test_utils::log::LoggerContext;

    use super::*;

    #[derive(Debug)]
    struct ExampleError;

    mod error {
        use super::*;

        #[test]
        fn new() {
            let logger_context = LoggerContext::new();

            let error = Error::new("name", "description", ExampleError);

            error.show_error();

            logger_context.assert_entry(Level::Error, "Error in name: description (ExampleError)");
        }

        #[test]
        fn new_test() {
            let logger_context = LoggerContext::new();

            let error = Error::new_test("name", "description", ExampleError);

            error.show_error();

            logger_context.assert_entry(Level::Error, "Error in name: description (ExampleError)");
        }

        #[test]
        fn new_custom() {
            let logger_context = LoggerContext::new();

            let error = Error::new_custom("name", "description");

            error.show_error();

            logger_context.assert_entry(Level::Error, "Error in name: description");
        }
    }

    mod wrap_error_ext {
        use super::*;

        mod result {
            use super::*;

            #[test]
            fn when_ok() {
                let result: StdResult<u32, ExampleError> = Ok(42);

                assert_that!(
                    result.wrap_error("name", "description"),
                    is(equal_to(Ok(42)))
                );
            }

            #[test]
            fn when_err() {
                let result: StdResult<u32, ExampleError> = Err(ExampleError);

                assert_that!(
                    result.wrap_error("name", "description"),
                    is(equal_to(Err(Error::new(
                        "name",
                        "description",
                        ExampleError
                    ))))
                );
            }

            #[test]
            fn when_custom_error() {
                let result: StdResult<u32, Error> = Err(Error::new_custom("inner", "inner desc"));

                assert_that!(
                    result.wrap_error("name", "description"),
                    is(equal_to(Err(Error::new(
                        "name",
                        "description",
                        Error::new_custom("inner", "inner desc")
                    ))))
                );
            }
        }

        mod option {
            use super::*;

            #[test]
            fn when_some() {
                let option = Some(42);

                assert_that!(
                    option.wrap_error("name", "description"),
                    is(equal_to(Ok(42)))
                );
            }

            #[test]
            fn when_err() {
                let option: Option<u32> = None;

                assert_that!(
                    option.wrap_error("name", "description"),
                    is(equal_to(Err(Error::new_custom("name", "description"))))
                );
            }
        }
    }

    mod result_ext {
        use super::*;

        #[test]
        fn show_error_when_err() {
            let logger_context = LoggerContext::new();

            let result: Result<u32> = Err(Error::new_custom("name", "description"));

            result.show_error_and_ignore();

            logger_context.assert_entry(Level::Error, "Error in name: description");
        }
    }
}