grid_sdk/error/
internal.rs

1// Copyright 2018-2020 Cargill Incorporated
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Module containing InternalError implementation.
16
17use std::error;
18use std::fmt;
19
20struct Source {
21    prefix: Option<String>,
22    source: Box<dyn error::Error>,
23}
24
25/// An error which is returned for reasons internal to the function.
26///
27/// This error is produced when a failure occurred within the function but the failure is due to an
28/// internal implementation detail of the function. This generally means that there is no specific
29/// information which can be returned that would help the caller of the function recover or
30/// otherwise take action.
31pub struct InternalError {
32    message: Option<String>,
33    source: Option<Source>,
34}
35
36impl InternalError {
37    /// Constructs a new `InternalError` from a specified source error.
38    ///
39    /// The implementation of `std::fmt::Display` for this error will simply pass through the
40    /// display of the source message unmodified.
41    ///
42    /// # Examples
43    ///
44    /// ```
45    /// use grid_sdk::error::InternalError;
46    ///
47    /// let io_err = std::io::Error::new(std::io::ErrorKind::Other, "io error");
48    /// let internal_error = InternalError::from_source(Box::new(io_err));
49    /// assert_eq!(format!("{}", internal_error), "io error");
50    /// ```
51    pub fn from_source(source: Box<dyn error::Error>) -> Self {
52        Self {
53            message: None,
54            source: Some(Source {
55                prefix: None,
56                source,
57            }),
58        }
59    }
60
61    /// Constructs a new `InternalError` from a specified source error and message string.
62    ///
63    /// The implementation of `std::fmt::Display` for this error will be the message string
64    /// provided.
65    ///
66    /// # Examples
67    ///
68    /// ```
69    /// use grid_sdk::error::InternalError;
70    ///
71    /// let io_err = std::io::Error::new(std::io::ErrorKind::Other, "io error");
72    /// let internal_error = InternalError::from_source_with_message(Box::new(io_err), "oops".to_string());
73    /// assert_eq!(format!("{}", internal_error), "oops");
74    /// ```
75    pub fn from_source_with_message(source: Box<dyn error::Error>, message: String) -> Self {
76        Self {
77            message: Some(message),
78            source: Some(Source {
79                prefix: None,
80                source,
81            }),
82        }
83    }
84
85    /// Constructs a new `InternalError` from a specified source error and prefix string.
86    ///
87    /// The implementation of `std::fmt::Display` for this error will be constructed from the
88    /// prefix and source message's display following the format of `format!("{}: {}", prefix,
89    /// source)`.
90    ///
91    /// # Examples
92    ///
93    /// ```
94    /// use grid_sdk::error::InternalError;
95    ///
96    /// let io_err = std::io::Error::new(std::io::ErrorKind::Other, "io error");
97    /// let internal_error = InternalError::from_source_with_prefix(Box::new(io_err), "Could not open file".to_string());
98    /// assert_eq!(format!("{}", internal_error), "Could not open file: io error");
99    /// ```
100    pub fn from_source_with_prefix(source: Box<dyn error::Error>, prefix: String) -> Self {
101        Self {
102            message: None,
103            source: Some(Source {
104                prefix: Some(prefix),
105                source,
106            }),
107        }
108    }
109
110    /// Constructs a new `InternalError` with a specified message string.
111    ///
112    /// The implementation of `std::fmt::Display` for this error will be the message string
113    /// provided.
114    ///
115    /// # Examples
116    ///
117    /// ```
118    /// use grid_sdk::error::InternalError;
119    ///
120    /// let internal_error = InternalError::with_message("oops".to_string());
121    /// assert_eq!(format!("{}", internal_error), "oops");
122    /// ```
123    pub fn with_message(message: String) -> Self {
124        Self {
125            message: Some(message),
126            source: None,
127        }
128    }
129}
130
131#[cfg(feature = "client-reqwest")]
132impl From<reqwest::Error> for InternalError {
133    fn from(err: reqwest::Error) -> Self {
134        InternalError::with_message(format!("{}", err))
135    }
136}
137
138impl error::Error for InternalError {
139    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
140        self.source.as_ref().map(|s| s.source.as_ref())
141    }
142}
143
144impl fmt::Display for InternalError {
145    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
146        match &self.message {
147            Some(m) => write!(f, "{}", m),
148            None => match &self.source {
149                Some(s) => match &s.prefix {
150                    Some(p) => write!(f, "{}: {}", p, s.source),
151                    None => write!(f, "{}", s.source),
152                },
153                None => write!(f, "{}", std::any::type_name::<InternalError>()),
154            },
155        }
156    }
157}
158
159impl fmt::Debug for InternalError {
160    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
161        const TYPE_NAME: &str = "InternalError";
162
163        match &self.message {
164            Some(m) => match &self.source {
165                Some(s) => write!(
166                    f,
167                    "{} {{ message: {:?}, source: {:?} }}",
168                    TYPE_NAME, m, s.source
169                ),
170                None => write!(f, "{} {{ message: {:?} }}", TYPE_NAME, m),
171            },
172            None => match &self.source {
173                Some(s) => match &s.prefix {
174                    Some(p) => write!(
175                        f,
176                        "{} {{ prefix: {:?}, source: {:?} }}",
177                        TYPE_NAME, p, s.source
178                    ),
179                    None => write!(f, "{} {{ source: {:?} }}", TYPE_NAME, s.source),
180                },
181                None => write!(f, "{}", TYPE_NAME),
182            },
183        }
184    }
185}
186
187#[cfg(test)]
188pub mod tests {
189    use super::*;
190
191    /// Tests that errors constructed with `InternalError::from_source` return a debug string of
192    /// the form `format!("InternalError { {:?} }", source)`.
193    #[test]
194    fn test_debug_from_source() {
195        let msg = "test message";
196        let debug = "InternalError { source: InternalError { message: \"test message\" } }";
197        let err =
198            InternalError::from_source(Box::new(InternalError::with_message(msg.to_string())));
199        assert_eq!(format!("{:?}", err), debug);
200    }
201
202    /// Tests that errors constructed with `InternalError::from_source_with_message` return a debug
203    /// string of the form `format!("InternalError { message: {:?}, source: {:?} }", message,
204    /// source)`.
205    #[test]
206    fn test_debug_from_source_with_message() {
207        let msg = "test message";
208        let debug = "InternalError { message: \"test message\", source: InternalError { message: \"unused\" } }";
209        let err = InternalError::from_source_with_message(
210            Box::new(InternalError::with_message("unused".to_string())),
211            msg.to_string(),
212        );
213        assert_eq!(format!("{:?}", err), debug);
214    }
215
216    /// Tests that errors constructed with `InternalError::from_source_with_prefix` return a debug
217    /// string of the form `format!("InternalError { prefix: {:?}, source: {:?} }", prefix,
218    /// source)`.
219    #[test]
220    fn test_debug_from_source_with_prefix() {
221        let prefix = "test prefix";
222        let msg = "test message";
223        let debug = "InternalError { prefix: \"test prefix\", source: InternalError { message: \"test message\" } }";
224        let err = InternalError::from_source_with_prefix(
225            Box::new(InternalError::with_message(msg.to_string())),
226            prefix.to_string(),
227        );
228        assert_eq!(format!("{:?}", err), debug);
229    }
230
231    /// Tests that errors constructed with `InternalError::with_message` return a debug
232    /// string of the form `format!("InternalError { message: {:?} }", message)`.
233    #[test]
234    fn test_debug_with_message() {
235        let msg = "test message";
236        let debug = "InternalError { message: \"test message\" }";
237        let err = InternalError::with_message(msg.to_string());
238        assert_eq!(format!("{:?}", err), debug);
239    }
240
241    /// Tests that error constructed with `InternalError::from_source` return a display
242    /// string which is the same as the source's display string.
243    #[test]
244    fn test_display_from_source() {
245        let msg = "test message";
246        let err =
247            InternalError::from_source(Box::new(InternalError::with_message(msg.to_string())));
248        assert_eq!(format!("{}", err), msg);
249    }
250
251    /// Tests that error constructed with `InternalError::from_source_with_message` return
252    /// message as the display string.
253    #[test]
254    fn test_display_from_source_with_message() {
255        let msg = "test message";
256        let err = InternalError::from_source_with_message(
257            Box::new(InternalError::with_message("unused".to_string())),
258            msg.to_string(),
259        );
260        assert_eq!(format!("{}", err), msg);
261    }
262
263    /// Tests that error constructed with `InternalError::from_source_with_message` return
264    /// a display string of the form `format!("{}: {}", prefix, source)`.
265    #[test]
266    fn test_display_from_source_with_prefix() {
267        let prefix = "test prefix";
268        let msg = "test message";
269        let err = InternalError::from_source_with_prefix(
270            Box::new(InternalError::with_message(msg.to_string())),
271            prefix.to_string(),
272        );
273        assert_eq!(format!("{}", err), format!("{}: {}", prefix, msg));
274    }
275
276    /// Tests that error constructed with `InternalError::with_message` return message as the
277    /// display string.
278    #[test]
279    fn test_display_with_message() {
280        let msg = "test message";
281        let err = InternalError::with_message(msg.to_string());
282        assert_eq!(format!("{}", err), msg);
283    }
284}