    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
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117

    use std::{env::VarError, io, num::ParseIntError, path::PathBuf, str::ParseBoolError};

    use derive_more::{Display, Error};
    use toml::de::Error as TomlError;

    /// Errors that can be returned from methods in this crate.
    #[derive(Debug, Display, Error)]
    pub enum Error {
        /// Environment variable does not exists or is invalid.
        #[display(fmt = "Env var error: {_0}")]
        EnvVarError(VarError),

        /// File already exists on disk.
        #[display(fmt = "File exists: {}", "_0.display()")]
        FileExists(#[error(not(source))] PathBuf),

        /// Invalid value.
        #[allow(missing_docs)]
        #[display(fmt = "Expected {expected}, got {got} (@ {file}:{line}:{column})")]
        InvalidValue {
            expected: &'static str,
            got: String,
            file: &'static str,
            line: u32,
            column: u32,
        },

        /// I/O error.
        #[display(fmt = "")]
        IoError(io::Error),

        /// Value is not a boolean.
        #[display(fmt = "Failed to parse boolean: {_0}")]
        ParseBoolError(ParseBoolError),

        /// Value is not an integer.
        #[display(fmt = "Failed to parse integer: {_0}")]
        ParseIntError(ParseIntError),

        /// Value is not an address.
        #[display(fmt = "Failed to parse address: {_0}")]
        ParseAddressError(#[error(not(source))] String),

        /// Error deserializing as TOML.
        #[display(fmt = "TOML error: {_0}")]
        TomlError(TomlError),
    }

    macro_rules! InvalidValue {
        (expected: $expected:expr, got: $got:expr,) => {
            crate::Error::InvalidValue {
                expected: $expected,
                got: $got.to_string(),
                file: file!(),
                line: line!(),
                column: column!(),
            }
        };
    }

    impl From<io::Error> for Error {
        fn from(err: io::Error) -> Self {
            Self::IoError(err)
        }
    }

    impl From<ParseBoolError> for Error {
        fn from(err: ParseBoolError) -> Self {
            Self::ParseBoolError(err)
        }
    }

    impl From<ParseIntError> for Error {
        fn from(err: ParseIntError) -> Self {
            Self::ParseIntError(err)
        }
    }

    impl From<TomlError> for Error {
        fn from(err: TomlError) -> Self {
            Self::TomlError(err)
        }
    }

    impl From<VarError> for Error {
        fn from(err: VarError) -> Self {
            Self::EnvVarError(err)
        }
    }

    impl From<Error> for io::Error {
        fn from(err: Error) -> Self {
            match err {
                Error::EnvVarError(_) => io::Error::new(io::ErrorKind::InvalidInput, err.to_string()),

                Error::FileExists(_) => io::Error::new(io::ErrorKind::AlreadyExists, err.to_string()),

                Error::InvalidValue { .. } => {
                    io::Error::new(io::ErrorKind::InvalidInput, err.to_string())
                }

                Error::IoError(io_error) => io_error,

                Error::ParseBoolError(_) => {
                    io::Error::new(io::ErrorKind::InvalidInput, err.to_string())
                }

                Error::ParseIntError(_) => io::Error::new(io::ErrorKind::InvalidInput, err.to_string()),

                Error::ParseAddressError(_) => {
                    io::Error::new(io::ErrorKind::InvalidInput, err.to_string())
                }

                Error::TomlError(_) => io::Error::new(io::ErrorKind::InvalidInput, err.to_string()),
            }
        }
    }