minimo 0.5.42

terminal ui library combining alot of things from here and there and making it slightly easier to play with
Documentation
use std::error::Error as StdError;
use std::fmt::{self, Debug, Display};
use std::ops::Not;

/// Type alias for Result using the custom Error type
pub type Result<T> = std::result::Result<T, Error>;

/// Custom Error type replacing `thiserror`
#[derive(Debug)]
pub enum Error {
    /// Simple error with a message
    Simple(String),
    /// Detailed error with additional context
    Detailed(DetailedError),
    /// Custom error wrapping any standard error
    Custom(Box<dyn StdError + Send + Sync>),
}

impl Error {
    /// Creates a simple error with a message
    pub fn simple<T: Display>(message: T) -> Self {
        Self::Simple(message.to_string())
    }

    /// Creates a detailed error with additional context
    pub fn detailed(details: DetailedError) -> Self {
        Self::Detailed(details)
    }

    /// Creates a custom error from any type that implements StdError
    pub fn custom<T: StdError + Send + Sync + 'static>(error: T) -> Self {
        Self::Custom(Box::new(error))
    }

    /// Prints the error to the standard output
    pub fn print(&self) {
        match self {
            Self::Simple(message) => println!("Error: {}", message),
            Self::Detailed(details) => details.print(),
            Self::Custom(error) => println!("Error: {}", error),
        }
    }
}

impl Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Simple(message) => write!(f, "{}", message),
            Self::Detailed(details) => Display::fmt(details, f),
            Self::Custom(error) => Display::fmt(error, f),
        }
    }
}

impl StdError for Error {
    fn source(&self) -> Option<&(dyn StdError + 'static)> {
        match self {
            Self::Custom(err) => err.source(),
            _ => None,
        }
    }
}

/// Struct for detailed errors with context and help messages
#[derive(Debug, Clone)]
pub struct DetailedError {
    message: String,
    help: Option<String>,
    context: Vec<(String, String)>,
}

impl DetailedError {
    /// Creates a new DetailedError with a message
    pub fn new<T: Display>(message: T) -> Self {
        Self {
            message: message.to_string(),
            help: None,
            context: Vec::new(),
        }
    }

    /// Adds a help message to the DetailedError
    pub fn help<T: Display>(mut self, help: T) -> Self {
        self.help = Some(help.to_string());
        self
    }

    /// Adds contextual information as key-value pairs
    pub fn with<T: Display, U: Display>(mut self, key: T, value: U) -> Self {
        self.context.push((key.to_string(), value.to_string()));
        self
    }

    /// Prints the detailed error to the standard output
    pub fn print(&self) {
        println!("Error: {}", self.message);
        for (key, value) in &self.context {
            println!("{}: {}", key, value);
        }
        if let Some(help) = &self.help {
            println!("Help: {}", help);
        }
    }
}

impl Display for DetailedError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.message)?;
        if !self.context.is_empty() {
            write!(f, " (")?;
            for (i, (key, value)) in self.context.iter().enumerate() {
                if i > 0 {
                    write!(f, ", ")?;
                }
                write!(f, "{}: {}", key, value)?;
            }
            write!(f, ")")?;
        }
        if let Some(help) = &self.help {
            write!(f, " - {}", help)?;
        }
        Ok(())
    }
}

// Implement From traits for easy conversion into the custom Error type

impl From<String> for Error {
    fn from(s: String) -> Self {
        Error::Simple(s)
    }
}

impl From<&str> for Error {
    fn from(s: &str) -> Self {
        Error::Simple(s.to_string())
    }
}

// Remove the generic From<E> for Error to avoid conflicts
// Implement From for specific error types instead

impl From<std::io::Error> for Error {
    fn from(error: std::io::Error) -> Self {
        Error::Custom(Box::new(error))
    }
}

 // Define a marker trait
pub trait CustomError: StdError + Send + Sync + 'static {}
impl<T> CustomError for T where T: StdError + Send + Sync + 'static {}

 


// Add more specific From implementations as needed

/// Helper trait to convert standard Results into custom Results
pub trait IntoResult<T> {
    /// Converts `self` into `Result<T, Error>`
    fn into_result(self) -> Result<T>;
}

impl<T, E> IntoResult<T> for std::result::Result<T, E>
where
    E: Into<Error>,
{
    fn into_result(self) -> Result<T> {
        self.map_err(Into::into)
    }
}

// Example Usage
#[cfg(test)]
mod tests {
    use super::*;
    use std::fs::File;

    #[test]
    fn test_simple_error() {
        let err = Error::simple("A simple error occurred");
        err.print();
        assert_eq!(format!("{}", err), "A simple error occurred");
    }

    #[test]
    fn test_detailed_error() {
        let err = DetailedError::new("A detailed error occurred")
            .with("File", "config.toml")
            .with("Line", "42")
            .help("Please check the configuration file for errors");
        let error = Error::detailed(err.clone());
        error.print();
        assert_eq!(
            format!("{}", error),
            "A detailed error occurred (File: config.toml, Line: 42) - Please check the configuration file for errors"
        );
    }

    #[test]
    fn test_custom_error() {
        let file_error = File::open("non_existent_file.txt").unwrap_err();
        let error = Error::custom(file_error);
        error.print();
        assert!(format!("{}", error).contains("No such file or directory"));
    }

    #[test]
    fn test_into_result() -> Result<()> {
        // Example function that returns a standard Result
        fn might_fail(flag: bool) -> std::result::Result<(), &'static str> {
            if flag {
                Ok(())
            } else {
                Err("Something went wrong")
            }
        }

        // Using the IntoResult trait to convert it into the custom Result type
        let result = might_fail(false).into_result();

        match result {
            Ok(_) => {
                // Optionally, you can print a success message or do nothing
                println!("Operation succeeded.");
            }
            Err(e) => {
                // Call the print method on the Error
                e.print();
            }
        }

        Ok(())
    }
}