Skip to main content

minicbor/encode/
error.rs

1use core::fmt;
2
3#[cfg(feature = "alloc")]
4use alloc::string::ToString;
5
6#[cfg(feature = "alloc")]
7use alloc::boxed::Box;
8
9/// Encoding error.
10#[derive(Debug)]
11pub struct Error<E> {
12    err: ErrorImpl<E>,
13    #[cfg(not(feature = "alloc"))]
14    msg: &'static str,
15    #[cfg(feature = "alloc")]
16    msg: alloc::string::String
17}
18
19impl<E> Error<E> {
20    /// Construct an error with a generic message.
21    ///
22    /// With feature `"alloc"` any type `T: Display` is accepted which allows
23    /// formatted strings. Otherwise only a `&'static str` can be used as a
24    /// message.
25    #[cfg(not(feature = "alloc"))]
26    pub fn message(msg: &'static str) -> Self {
27        Error { err: ErrorImpl::Message, msg }
28    }
29
30    /// Construct an error with a generic message.
31    ///
32    /// With feature `"alloc"` any type `T: Display` is accepted which allows
33    /// formatted strings. Otherwise only a `&'static str` can be used as a
34    /// message.
35    #[cfg(feature = "alloc")]
36    pub fn message<T: fmt::Display>(msg: T) -> Self {
37        Error { err: ErrorImpl::Message, msg: msg.to_string() }
38    }
39
40    /// A write error happened.
41    pub fn write(e: E) -> Self {
42        Error { err: ErrorImpl::Write(e), msg: Default::default() }
43    }
44
45    /// A custom error.
46    ///
47    /// *Requires feature* `"alloc"`.
48    #[cfg(feature = "alloc")]
49    pub fn custom<T: core::error::Error + Send + Sync + 'static>(err: T) -> Self {
50        Error { err: ErrorImpl::Custom(Box::new(err)), msg: Default::default() }
51    }
52
53    /// Add a message to this error value.
54    ///
55    /// With feature `"alloc"` any type `T: Display` is accepted which allows
56    /// formatted strings. Otherwise only a `&'static str` can be used as a
57    /// message.
58    #[cfg(not(feature = "alloc"))]
59    pub fn with_message(mut self, msg: &'static str) -> Self {
60        self.msg = msg;
61        self
62    }
63
64    /// Add a message to this error value.
65    ///
66    /// With feature `"alloc"` any type `T: Display` is accepted which allows
67    /// formatted strings. Otherwise only a `&'static str` can be used as a
68    /// message.
69    #[cfg(feature = "alloc")]
70    pub fn with_message<T: fmt::Display>(mut self, msg: T) -> Self {
71        self.msg = msg.to_string();
72        self
73    }
74
75    pub fn is_message(&self) -> bool {
76        matches!(self.err, ErrorImpl::Message)
77    }
78
79    pub fn is_write(&self) -> bool {
80        matches!(self.err, ErrorImpl::Write(_))
81    }
82
83    #[cfg(feature = "alloc")]
84    pub fn is_custom(&self) -> bool {
85        matches!(self.err, ErrorImpl::Custom(_))
86    }
87
88    /// Extract the inner `Write` error if possible.
89    pub fn as_write(&self) -> Option<&E> {
90        if let ErrorImpl::Write(e) = &self.err {
91            Some(e)
92        } else {
93            None
94        }
95    }
96
97    /// Consume this error and return the inner `Write` error if possible.
98    pub fn into_write(self) -> Option<E> {
99        if let ErrorImpl::Write(e) = self.err {
100            Some(e)
101        } else {
102            None
103        }
104    }
105}
106
107/// Internal error representation.
108#[derive(Debug)]
109enum ErrorImpl<E> {
110    /// Error writing bytes to a `Write` impl.
111    Write(E),
112    /// Generic error message.
113    Message,
114    /// Custom error.
115    #[cfg(feature = "alloc")]
116    Custom(Box<dyn core::error::Error + Send + Sync>)
117}
118
119impl<E: fmt::Display> fmt::Display for Error<E> {
120    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
121        match &self.err {
122            ErrorImpl::Message => write!(f, "{}", self.msg),
123            #[cfg(not(feature = "std"))]
124            ErrorImpl::Write(e) =>
125                if self.msg.is_empty() {
126                    write!(f, "write error: {e}")
127                } else {
128                    write!(f, "write error: {e}, {}", self.msg)
129                }
130            #[cfg(feature = "std")]
131            ErrorImpl::Write(_) =>
132                if self.msg.is_empty() {
133                    write!(f, "write error")
134                } else {
135                    write!(f, "write error: {}", self.msg)
136                }
137            #[cfg(feature = "alloc")]
138            ErrorImpl::Custom(_) =>
139                if self.msg.is_empty() {
140                    write!(f, "encode error")
141                } else {
142                    write!(f, "encode error: {}", self.msg)
143                }
144        }
145    }
146}
147
148impl<E: core::error::Error + 'static> core::error::Error for Error<E> {
149    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
150        match &self.err {
151            ErrorImpl::Message   => None,
152            ErrorImpl::Write(e)  => Some(e),
153            #[cfg(feature = "alloc")]
154            ErrorImpl::Custom(e) => Some(&**e)
155        }
156    }
157}