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
use core::fmt;

/// An error describing a failed operation on an IP object.
#[derive(Clone, Copy, Debug)]
pub struct Error {
    kind: Kind,
    msg: Option<&'static str>,
    source: Option<SourceError>,
}

impl Error {
    pub(crate) fn new<S: AsRef<str> + ?Sized + 'static>(
        kind: Kind,
        msg: Option<&'static S>,
        source: Option<SourceError>,
    ) -> Self {
        Self {
            kind,
            msg: msg.map(S::as_ref),
            source,
        }
    }

    /// Returns the [`Kind`] of error.
    ///
    /// # Examples
    ///
    /// ``` rust
    /// use ip::{error::Kind, Address, Ipv4};
    ///
    /// let err = "10.0.0.256".parse::<Address<Ipv4>>().unwrap_err();
    /// assert_eq!(err.kind(), Kind::ParserError);
    /// ```
    #[must_use]
    pub const fn kind(&self) -> Kind {
        self.kind
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if let Some(msg) = self.msg {
            write!(f, "{}: {}", self.kind, msg)
        } else {
            self.kind.fmt(f)
        }
    }
}

#[cfg(feature = "std")]
#[allow(trivial_casts)]
impl std::error::Error for Error {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        self.source.map(|err| err as _)
    }
}

#[cfg(not(feature = "std"))]
impl Error {
    /// Returns the underyling source error, if it exists.
    ///
    /// This method is provided for interface compatibility with
    /// `std::error::Error` in a `no_std` environment.
    #[must_use]
    pub fn source(&self) -> Option<SourceError> {
        self.source
    }
}

/// The "kind" of an [`Error`].
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
pub enum Kind {
    /// An [`Error`] resulting from an operation on a prefix-length.
    PrefixLength,
    /// An [`Error`] resulting from a parser failure.
    ParserError,
    /// An [`Error`] resulting from an attempt to convert between incompatible
    /// address families.
    AfiMismatch,
    /// An [`Error`] resulting from an invalid prefix-length range.
    PrefixLengthRange,
}

impl fmt::Display for Kind {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::PrefixLength => {
                write!(f, "prefix-length out of bounds")
            }
            Self::ParserError => write!(f, "parser error"),
            Self::AfiMismatch => write!(f, "address family mis-match"),
            Self::PrefixLengthRange => write!(f, "invalid prefix-length range"),
        }
    }
}

#[cfg(feature = "std")]
type SourceError = &'static (dyn std::error::Error + Send + Sync + 'static);
#[cfg(not(feature = "std"))]
type SourceError = &'static (dyn core::any::Any);

macro_rules! err {
    ( $kind:expr ) => {
        $crate::error::Error::new::<&'static str>($kind, None, None)
    };
    ( $kind:expr, $msg:expr ) => {
        $crate::error::Error::new($kind, Some($msg), None)
    };
}
pub(crate) use err;

#[cfg(test)]
pub(crate) type TestResult = Result<(), Error>;