1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
use std::{error::Error, fmt, num::TryFromIntError, path::PathBuf, sync::LazyLock};

// https://gist.github.com/abritinthebay/d80eb99b2726c83feb0d97eab95206c4
// https://talyian.github.io/ansicolors/

// ANSI Color Codes:
pub const RED: &str = "\x1b[31m";
pub const GREEN: &str = "\x1b[32m";
pub const YELLOW: &str = "\x1b[33m";
pub const BLUE: &str = "\x1b[34m";
pub const RESET: &str = "\x1b[0m";

/// A value which is initialized on the first access
pub static ERROR: LazyLock<String> = LazyLock::new(|| format!("{RED}Error{RESET}"));
pub static HELP: LazyLock<String> =
    LazyLock::new(|| format!("For more information, try '{GREEN}--help{RESET}'."));

#[derive(Debug)]
/// The `WSError` enum defines the error values
///
/// <https://doc.rust-lang.org/rust-by-example/error/multiple_error_types/define_error_type.html>
pub enum WSError<'a> {
    /// Directory path must exist
    Parent(PathBuf),
    /// Unable to obtain minimum value
    MinValue,
    /// Missing value
    MissingValue(&'a str),
    /// Invalid value
    InvalidValue(&'a str),
    /// At least value
    AtLeastValue(&'a str, u32),
    /// Unexpected argument
    UnexpectedArg(String),
    /// Try to performs the conversion
    TryInto(String),
}

impl fmt::Display for WSError<'_> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Closure with Parameter
        let green = |num: &u32| format!("{GREEN}{num}{RESET}");
        let yellow = |s: &str| format!("{YELLOW}{s}{RESET}");

        match self {
            WSError::Parent(path) => write!(f, "Wallpaper dir {path:?} does not exist."),
            WSError::MinValue => write!(f, "Unable to obtain minimum value!"),
            WSError::MissingValue(arg) => write!(
                f,
                "{e}: missing value for '{a}'\n\n{h}",
                e = *ERROR,
                a = yellow(arg),
                h = *HELP
            ),
            WSError::InvalidValue(arg) => write!(
                f,
                "{e}: invalid value for '{a}'\n\n{h}",
                e = *ERROR,
                a = yellow(arg),
                h = *HELP
            ),
            WSError::AtLeastValue(arg, num) => {
                write!(
                    f,
                    "{e}: '{a}' must be at least {n}\n\n{h}",
                    e = *ERROR,
                    a = yellow(arg),
                    n = green(num),
                    h = *HELP
                )
            }
            WSError::UnexpectedArg(arg) => {
                write!(
                    f,
                    "{e}: unexpected argument '{a}' found\n\n{h}",
                    e = *ERROR,
                    a = yellow(arg),
                    h = *HELP
                )
            }
            WSError::TryInto(err) => write!(f, "{err}"),
        }
    }
}

impl Error for WSError<'_> {}

// Implementing the From trait for multiple types that you want to map into WSError.
// https://stackoverflow.com/questions/62238827/less-verbose-type-for-map-err-closure-argument
// let monitor: u8 = value.try_into().map_err(WSError::from)?;

impl From<TryFromIntError> for WSError<'_> {
    fn from(err: TryFromIntError) -> Self {
        Self::TryInto(err.to_string())
    }
}