fisher_common/
errors.rs

1// Copyright (C) 2016-2017 Pietro Albini
2//
3// This program is free software: you can redistribute it and/or modify
4// it under the terms of the GNU General Public License as published by
5// the Free Software Foundation, either version 3 of the License, or
6// (at your option) any later version.
7//
8// This program is distributed in the hope that it will be useful,
9// but WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11// GNU General Public License for more details.
12//
13// You should have received a copy of the GNU General Public License
14// along with this program.  If not, see <http://www.gnu.org/licenses/>.
15
16//! Error handling for Fisher.
17//!
18//! This module contains the definition of the [`Error`](struct.Error.html)
19//! struct, which wraps all the details about any kind of error occuring in
20//! Fisher. There is also the [`ErrorKind`](enum.ErrorKind.html) enum, which
21//! contains exactly the kind of error occured.
22
23use std::io;
24use std::fmt;
25use std::net;
26use std::num;
27use std::error::Error as StdError;
28use std::sync::mpsc;
29use std::sync;
30use std::result::Result as StdResult;
31
32use serde_json;
33use ansi_term::Colour;
34
35
36/// Convenience type alias to easily use Result with
37/// [`Error`](struct.Error.html).
38
39pub type Result<T> = StdResult<T, Error>;
40
41
42/// This enum represents the kind of error that occured, with the details
43/// about it.
44
45#[derive(Debug)]
46pub enum ErrorKind {
47    /// The provider requested by an hook doesn't exist. The provider name is
48    /// provided as the first parameter.
49    ProviderNotFound(String),
50
51    /// The input you provided was invalid. A more detailed error message is
52    /// available in the first parameter.
53    InvalidInput(String),
54
55    /// The current request didn't travel across the configured number of
56    /// proxies. This means the request was forged or the server is
57    /// misconfigured.
58    NotBehindProxy,
59
60    /// The current request isn't of the required kind.
61    WrongRequestKind,
62
63    /// The character is not valid hex. The character is available in the
64    /// first parameter.
65    InvalidHexChar(char),
66
67    /// The hex string has the wrong length.
68    InvalidHexLength,
69
70    /// An internal communication channel is broken.
71    BrokenChannel,
72
73    /// An internal lock is poisoned, probably due to a thread crash.
74    PoisonedLock,
75
76    /// An internal thread crashed.
77    ThreadCrashed,
78
79    /// An error occured while performing I/O operations. The underlying error
80    /// is available as the first parameter.
81    IoError(io::Error),
82
83    /// An error occured while parsing some JSON. The underlying error is
84    /// available as the first parameter.
85    JsonError(serde_json::Error),
86
87    /// An error occured while parsing an IP address. The underlying error is
88    /// available as the first parameter.
89    AddrParseError(net::AddrParseError),
90
91    /// An error occured while parsing a number. The underlying error is
92    /// available as the first parameter.
93    ParseIntError(num::ParseIntError),
94
95    /// A generic error, without a defined type
96    GenericError(Box<StdError + Send + Sync>),
97
98    #[doc(hidden)]
99    Dummy,
100}
101
102impl fmt::Display for ErrorKind {
103
104    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
105        write!(f, "{}", match *self {
106
107            ErrorKind::ProviderNotFound(ref provider) =>
108                format!("Provider {} not found", provider),
109
110            ErrorKind::InvalidInput(ref error) =>
111                format!("invalid input: {}", error),
112
113            ErrorKind::NotBehindProxy =>
114                "not behind the proxies".into(),
115
116            ErrorKind::WrongRequestKind =>
117                "wrong request kind".into(),
118
119            ErrorKind::InvalidHexChar(chr) =>
120                format!("{} is not valid hex", chr),
121
122            ErrorKind::InvalidHexLength =>
123                "invalid length of the hex".into(),
124
125            ErrorKind::BrokenChannel =>
126                "an internal communication channel crashed".into(),
127
128            ErrorKind::PoisonedLock =>
129                "an internal lock was poisoned".into(),
130
131            ErrorKind::ThreadCrashed =>
132                "an internal thread crashed".into(),
133
134            ErrorKind::IoError(ref error) =>
135                format!("{}", error),
136
137            ErrorKind::JsonError(ref error) =>
138                format!("{}", error),
139
140            ErrorKind::AddrParseError(ref error) =>
141                format!("{}", error),
142
143            ErrorKind::ParseIntError(..) =>
144                "you didn't provide a valid number".into(),
145
146            ErrorKind::GenericError(ref error) =>
147                format!("{}", error),
148
149            ErrorKind::Dummy =>
150                "dummy_error".into(),
151        })
152    }
153}
154
155
156
157
158/// This enum represents where the error occured.
159
160#[derive(Debug, PartialEq, Eq)]
161pub enum ErrorLocation {
162    /// The error occured in a file. The file name is available in the first
163    /// parameter, while the line number (if present) is available in the
164    /// second one.
165    File(String, Option<u32>),
166
167    /// The error occured while processing an hook. The hook name is available
168    /// in the first parameter.
169    HookProcessing(String),
170
171    /// There is no information about where the error occured.
172    Unknown,
173
174    #[doc(hidden)]
175    __NonExaustiveMatch,
176}
177
178impl fmt::Display for ErrorLocation {
179
180    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
181        match *self {
182
183            ErrorLocation::File(ref path, line) => {
184                write!(f, "file {}", path)?;
185                if let Some(num) = line {
186                    write!(f, ", on line {}", num)?;
187                }
188
189                Ok(())
190            },
191
192            ErrorLocation::HookProcessing(ref name) => {
193                write!(f, "while processing {}", name)
194            },
195
196            ErrorLocation::Unknown => {
197                write!(f, "")
198            },
199
200            ErrorLocation::__NonExaustiveMatch => {
201                panic!("You shouldn't use this.");
202            },
203        }
204    }
205}
206
207
208/// This class represents an error that occured in Fisher.
209///
210/// It contains all the details known about it, and you can either access it
211/// programmatically or display the error message to the user, already
212/// formatted. It also support automatic conversion from the error types of
213/// the libraries Fisher depends on.
214
215#[derive(Debug)]
216pub struct Error {
217    kind: ErrorKind,
218    location: ErrorLocation,
219}
220
221impl Error {
222
223    /// Create a new error. You need to provide the kind of error that occured.
224    ///
225    /// ## Example
226    ///
227    /// ```rust
228    /// # use fisher_common::errors::{Result, Error, ErrorKind};
229    /// fn my_function() -> Result<()> {
230    ///     let error = Error::new(ErrorKind::Dummy);
231    ///     Err(error)
232    /// }
233    /// # fn main() {
234    /// #   assert!(my_function().is_err());
235    /// # }
236    /// ```
237    pub fn new(kind: ErrorKind) -> Self {
238        Error {
239            kind: kind,
240            location: ErrorLocation::Unknown,
241        }
242    }
243
244    /// Set the location where the error occured.
245    pub fn set_location(&mut self, location: ErrorLocation) {
246        self.location = location;
247    }
248
249    /// Get the location where the error occured. You can either access it
250    /// programmatically or print a pretty version of it to the user.
251    pub fn location(&self) -> &ErrorLocation {
252        &self.location
253    }
254
255    /// Get the kind of error occured. You can either access it
256    /// programmatically or print a pretty version of it to the user.
257    pub fn kind(&self) -> &ErrorKind {
258        &self.kind
259    }
260
261    /// Show a nicely-formatted version of the error, usually for printing
262    /// it to the user. The function uses ANSI formatting codes.
263    ///
264    /// ```rust
265    /// # use fisher_common::errors::{Result, Error, ErrorKind};
266    /// # fn do_work() -> Result<()> {
267    /// #   Err(Error::new(ErrorKind::Dummy))
268    /// # }
269    /// if let Err(error) = do_work() {
270    ///     error.pretty_print();
271    /// }
272    /// ```
273    pub fn pretty_print(&self) {
274        println!("{} {}",
275            Colour::Red.bold().paint("Error:"),
276            self
277        );
278        if self.location != ErrorLocation::Unknown {
279            println!("{} {}",
280                Colour::Yellow.bold().paint("Location:"),
281                self.location
282            );
283        }
284    }
285}
286
287impl fmt::Display for Error {
288
289    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
290        write!(f, "{}", self.kind)
291    }
292}
293
294impl StdError for Error {
295
296    fn description(&self) -> &str {
297        match self.kind {
298            ErrorKind::ProviderNotFound(..) =>
299                "provider not found",
300            ErrorKind::InvalidInput(..) =>
301                "invalid input",
302            ErrorKind::NotBehindProxy =>
303                "not behind the proxies",
304            ErrorKind::WrongRequestKind =>
305                "wrong request kind",
306            ErrorKind::InvalidHexChar(..) =>
307                "invalid character in hex",
308            ErrorKind::InvalidHexLength =>
309                "invalid length of the hex",
310            ErrorKind::BrokenChannel =>
311                "internal communication channel crashed",
312            ErrorKind::PoisonedLock =>
313                "poisoned lock",
314            ErrorKind::ThreadCrashed =>
315                "thread crashed",
316            ErrorKind::IoError(ref error) =>
317                error.description(),
318            ErrorKind::JsonError(ref error) =>
319                error.description(),
320            ErrorKind::AddrParseError(ref error) =>
321                error.description(),
322            ErrorKind::ParseIntError(..) =>
323                "invalid number",
324            ErrorKind::GenericError(ref error) =>
325                error.description(),
326            ErrorKind::Dummy =>
327                "dummy error",
328        }
329    }
330
331    fn cause(&self) -> Option<&StdError> {
332        match self.kind {
333            ErrorKind::IoError(ref error) => Some(error as &StdError),
334            ErrorKind::JsonError(ref error) => Some(error as &StdError),
335            ErrorKind::AddrParseError(ref error) => Some(error as &StdError),
336            ErrorKind::ParseIntError(ref error) => Some(error as &StdError),
337            _ => None,
338        }
339    }
340}
341
342macro_rules! derive_error {
343    ($from:path, $to:path) => {
344        impl From<$from> for Error {
345
346            fn from(error: $from) -> Self {
347                Error::new($to(error))
348            }
349        }
350    };
351}
352
353impl From<ErrorKind> for Error {
354
355    fn from(error: ErrorKind) -> Self {
356        Error::new(error)
357    }
358}
359
360impl From<mpsc::RecvError> for Error {
361
362    fn from(_: mpsc::RecvError) -> Self {
363        Error::new(ErrorKind::BrokenChannel)
364    }
365}
366
367impl<T> From<mpsc::SendError<T>> for Error {
368
369    fn from(_: mpsc::SendError<T>) -> Self {
370        Error::new(ErrorKind::BrokenChannel)
371    }
372}
373
374impl<T> From<sync::PoisonError<T>> for Error {
375
376    fn from(_: sync::PoisonError<T>) -> Self {
377        Error::new(ErrorKind::PoisonedLock)
378    }
379}
380
381derive_error!(io::Error, ErrorKind::IoError);
382derive_error!(serde_json::Error, ErrorKind::JsonError);
383derive_error!(net::AddrParseError, ErrorKind::AddrParseError);
384derive_error!(num::ParseIntError, ErrorKind::ParseIntError);
385derive_error!(Box<StdError + Send + Sync>, ErrorKind::GenericError);