Skip to main content

reachable/
error.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4//
5// Author: Simon Brummer (simon.brummer@posteo.de)
6
7//! Module containing all custom error types. All type shall implement [Error].
8
9// Imports
10use std::error::Error;
11use std::fmt::{self};
12use std::io::{self};
13use std::num::{self};
14
15// Documentation imports
16#[cfg(doc)]
17use super::Target;
18
19/// Alias for preallocated error messages
20pub type ErrorMessage = &'static str;
21
22/// Custom error type for a failed attempt to parse something into a [Target].
23#[derive(Debug)]
24pub enum ParseTargetError {
25    /// ParseTargetError containing a Message
26    Message(ErrorMessage),
27    /// ParseTargetError containing a Message and a [num::ParseIntError]
28    ParseIntError(ErrorMessage, num::ParseIntError),
29    /// ParseTargetError containing a Message and a trait object implementing [Error]
30    GenericError(ErrorMessage, Box<dyn Error>),
31}
32
33impl Error for ParseTargetError {
34    fn source(&self) -> Option<&(dyn Error + 'static)> {
35        match self {
36            ParseTargetError::Message(_) => None,
37            ParseTargetError::ParseIntError(_, ref error) => Some(error),
38            ParseTargetError::GenericError(_, ref error) => Some(error.as_ref()),
39        }
40    }
41}
42
43impl fmt::Display for ParseTargetError {
44    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
45        let error_message = match self {
46            ParseTargetError::Message(error_message)
47            | ParseTargetError::ParseIntError(error_message, _)
48            | ParseTargetError::GenericError(error_message, _) => error_message,
49        };
50
51        match self.source() {
52            None => write!(formatter, "{}", error_message),
53            Some(error) => write!(formatter, "{} caused by: {}", error_message, error),
54        }
55    }
56}
57
58impl From<ErrorMessage> for ParseTargetError {
59    fn from(message: ErrorMessage) -> Self {
60        ParseTargetError::Message(message)
61    }
62}
63
64impl From<(ErrorMessage, num::ParseIntError)> for ParseTargetError {
65    fn from(pieces: (ErrorMessage, num::ParseIntError)) -> Self {
66        let (msg, error) = pieces;
67        ParseTargetError::ParseIntError(msg, error)
68    }
69}
70
71impl From<(ErrorMessage, Box<dyn Error>)> for ParseTargetError {
72    fn from(pieces: (ErrorMessage, Box<dyn Error>)) -> Self {
73        let (msg, error) = pieces;
74        ParseTargetError::GenericError(msg, error)
75    }
76}
77
78impl From<Box<dyn Error>> for ParseTargetError {
79    fn from(error: Box<dyn Error>) -> Self {
80        ParseTargetError::from(("GenericError", error))
81    }
82}
83
84/// Custom error type for a failed attempt to resolve a [Target].
85#[derive(Debug)]
86pub enum ResolveTargetError {
87    /// ResolveTargetError containing a Message
88    Message(ErrorMessage),
89    /// ResolveTargetError containing a Message and an [io::Error]
90    IoError(ErrorMessage, io::Error),
91    /// ResolveTargetError containing a Message and a trait object implementing [Error]
92    GenericError(ErrorMessage, Box<dyn Error>),
93}
94
95impl Error for ResolveTargetError {
96    fn source(&self) -> Option<&(dyn Error + 'static)> {
97        match self {
98            ResolveTargetError::Message(_) => None,
99            ResolveTargetError::IoError(_, ref error) => Some(error),
100            ResolveTargetError::GenericError(_, ref error) => Some(error.as_ref()),
101        }
102    }
103}
104
105impl fmt::Display for ResolveTargetError {
106    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
107        let error_message = match self {
108            ResolveTargetError::Message(error_message)
109            | ResolveTargetError::IoError(error_message, _)
110            | ResolveTargetError::GenericError(error_message, _) => error_message,
111        };
112
113        match self.source() {
114            None => write!(formatter, "{}", error_message),
115            Some(error) => write!(formatter, "{} caused by: {}", error_message, error),
116        }
117    }
118}
119
120impl From<ErrorMessage> for ResolveTargetError {
121    fn from(message: ErrorMessage) -> Self {
122        ResolveTargetError::Message(message)
123    }
124}
125
126impl From<(ErrorMessage, io::Error)> for ResolveTargetError {
127    fn from(pieces: (ErrorMessage, io::Error)) -> Self {
128        let (msg, error) = pieces;
129        ResolveTargetError::IoError(msg, error)
130    }
131}
132
133impl From<io::Error> for ResolveTargetError {
134    fn from(error: io::Error) -> Self {
135        ResolveTargetError::from(("IoError", error))
136    }
137}
138
139impl From<(ErrorMessage, Box<dyn Error>)> for ResolveTargetError {
140    fn from(pieces: (ErrorMessage, Box<dyn Error>)) -> Self {
141        let (msg, error) = pieces;
142        ResolveTargetError::GenericError(msg, error)
143    }
144}
145
146impl From<Box<dyn Error>> for ResolveTargetError {
147    fn from(error: Box<dyn Error>) -> Self {
148        ResolveTargetError::from(("GenericError", error))
149    }
150}
151
152/// Custom error type for a failed attempt to check the availability of a [Target].
153#[derive(Debug)]
154pub enum CheckTargetError {
155    /// CheckTargetError containing a Message
156    Message(ErrorMessage),
157    /// CheckTargetError containing a Message and a [ResolveTargetError]
158    ResolveTargetError(ErrorMessage, ResolveTargetError),
159    /// CheckTargetError containing a Message and a trait object implementing [Error]
160    GenericError(ErrorMessage, Box<dyn Error>),
161}
162
163impl Error for CheckTargetError {
164    fn source(&self) -> Option<&(dyn Error + 'static)> {
165        match self {
166            CheckTargetError::Message(_) => None,
167            CheckTargetError::ResolveTargetError(_, ref error) => Some(error),
168            CheckTargetError::GenericError(_, ref error) => Some(error.as_ref()),
169        }
170    }
171}
172
173impl fmt::Display for CheckTargetError {
174    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
175        let error_message = match self {
176            CheckTargetError::Message(error_message)
177            | CheckTargetError::ResolveTargetError(error_message, _)
178            | CheckTargetError::GenericError(error_message, _) => error_message,
179        };
180
181        match self.source() {
182            None => write!(formatter, "{}", error_message),
183            Some(error) => write!(formatter, "{} caused by: {}", error_message, error),
184        }
185    }
186}
187
188impl From<ErrorMessage> for CheckTargetError {
189    fn from(message: ErrorMessage) -> Self {
190        CheckTargetError::Message(message)
191    }
192}
193
194impl From<(ErrorMessage, ResolveTargetError)> for CheckTargetError {
195    fn from(pieces: (ErrorMessage, ResolveTargetError)) -> Self {
196        let (msg, error) = pieces;
197        CheckTargetError::ResolveTargetError(msg, error)
198    }
199}
200
201impl From<ResolveTargetError> for CheckTargetError {
202    fn from(error: ResolveTargetError) -> Self {
203        CheckTargetError::from(("ResolveTargetError", error))
204    }
205}
206
207impl From<(ErrorMessage, Box<dyn Error>)> for CheckTargetError {
208    fn from(pieces: (ErrorMessage, Box<dyn Error>)) -> Self {
209        let (msg, error) = pieces;
210        CheckTargetError::GenericError(msg, error)
211    }
212}
213
214impl From<Box<dyn Error>> for CheckTargetError {
215    fn from(error: Box<dyn Error>) -> Self {
216        CheckTargetError::from(("GenericError", error))
217    }
218}
219
220#[cfg(test)]
221mod tests {
222    use super::*;
223
224    // ParseTargetError tests
225    #[test]
226    fn parse_target_error_from_str() {
227        // Expectency: A ParseTargetError must contain its error message.
228        assert_eq!(
229            format!("{}", ParseTargetError::from("Error Message!")),
230            "Error Message!"
231        );
232    }
233
234    #[test]
235    fn parse_target_error_from_parse_int_error() {
236        // Expectency: A ParseTargetError must contain its error message and the description
237        //             of the inner ParseIntError.
238        let error = i32::from_str_radix("invalid", 10).unwrap_err();
239        assert_eq!(
240            format!("{}", ParseTargetError::from(("ParseIntError!", error))),
241            "ParseIntError! caused by: invalid digit found in string"
242        );
243    }
244
245    #[test]
246    fn parse_target_error_from_boxed_error_trait_object() {
247        // Expectency: A ParseTargetError must contain its error message and the description
248        //             of the inner boxed error trait object.
249        let boxed_error: Box<dyn Error> = Box::new(io::Error::from(io::ErrorKind::AddrNotAvailable));
250        assert_eq!(
251            format!("{}", ParseTargetError::from(boxed_error)),
252            "GenericError caused by: address not available"
253        );
254    }
255
256    #[test]
257    fn parse_target_error_chain_multiple_errors() {
258        // Expectency: A ParseTargetError must recursively resolve its all its stored inner errors.
259        //             chaining them together into a single message
260        let error1: Box<dyn Error> = Box::new(ParseTargetError::from("Layer1!"));
261        let error2: Box<dyn Error> = Box::new(ParseTargetError::from(("Layer2!", error1)));
262        assert_eq!(
263            format!("{}", ParseTargetError::from(("Layer3!", error2))),
264            "Layer3! caused by: Layer2! caused by: Layer1!"
265        );
266    }
267
268    // ResolveTargetError tests
269    #[test]
270    fn resolve_target_error_from_str() {
271        // Expectency: A ResolveTargetError must contain its error message
272        assert_eq!(
273            format!("{}", ResolveTargetError::from("Error Message!")),
274            "Error Message!"
275        );
276    }
277
278    #[test]
279    fn resolve_target_error_from_parse_int_error() {
280        // Expectency: A ResolveTargetError must contain its error message and the description
281        //             of the inner io::Error.
282        assert_eq!(
283            format!("{}", ResolveTargetError::from(io::Error::from(io::ErrorKind::Other))),
284            "IoError caused by: other error"
285        );
286    }
287
288    #[test]
289    fn resolve_target_error_from_boxed_error_trait_object() {
290        // Expectency: A ResolveTargetError must contain its error message and the description
291        //             of the inner boxed error trait object.
292        let boxed_error: Box<dyn Error> = Box::new(ParseTargetError::from("ParseTargetError"));
293        assert_eq!(
294            format!("{}", ResolveTargetError::from(boxed_error)),
295            "GenericError caused by: ParseTargetError"
296        );
297    }
298
299    // CheckTargetError tests
300    #[test]
301    fn check_target_error_from_str() {
302        // Expectency: A CheckTargetError must contain its error message.
303        assert_eq!(
304            format!("{}", CheckTargetError::from("Error Message!")),
305            "Error Message!"
306        );
307    }
308
309    #[test]
310    fn check_target_error_from_resolve_target_error() {
311        // Expectency: A CheckTargetError must contain its error message and an instance of
312        //             ResolveTargetError
313        let resolve_target_error = ResolveTargetError::from(io::Error::from(io::ErrorKind::AddrNotAvailable));
314        assert_eq!(
315            format!("{}", CheckTargetError::from(resolve_target_error)),
316            "ResolveTargetError caused by: IoError caused by: address not available"
317        );
318    }
319
320    #[test]
321    fn check_target_error_from_boxed_error_trait_object() {
322        // Expectency: A CheckTargetError must contain its error message and the description
323        //             of the inner boxed error trait object.
324        let boxed_error: Box<dyn Error> = Box::new(io::Error::from(io::ErrorKind::AddrNotAvailable));
325        assert_eq!(
326            format!("{}", CheckTargetError::from(boxed_error)),
327            "GenericError caused by: address not available"
328        );
329    }
330
331    #[test]
332    fn check_target_error_via_questionmark_operator() {
333        // Expectency: Ensure conversion via Questionmark operator: Construct ResolveTargetError
334        //             from io::Error and then construct CheckTargetError from ResolveTargetError
335        fn returns_io_error() -> Result<u32, io::Error> {
336            Err(io::Error::from(io::ErrorKind::TimedOut))
337        }
338        fn returns_resolve_target_error() -> Result<u32, ResolveTargetError> {
339            Ok(returns_io_error()?)
340        }
341        fn returns_check_target_error() -> Result<u32, CheckTargetError> {
342            Ok(returns_resolve_target_error()?)
343        }
344        assert_eq!(
345            format!("{}", returns_check_target_error().unwrap_err()),
346            "ResolveTargetError caused by: IoError caused by: timed out"
347        );
348    }
349}