scutiger_core/
errors.rs

1#![allow(unknown_lints)]
2#![allow(bare_trait_objects)]
3#![allow(clippy::upper_case_acronyms)]
4
5use std::convert;
6use std::error;
7use std::fmt;
8use std::io;
9
10use git2;
11#[cfg(feature = "pcre")]
12use pcre2;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum ExitStatus {
16    Success = 0,
17    NonFatal = 1,
18    Fatal = 2,
19    ExternalProgramFailed = 3,
20}
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum ErrorKind {
24    NoSuchRevision,
25    Conflict,
26    GitError,
27    PCREError,
28    IOError,
29    BadPktlineHeader,
30    InvalidPacket,
31    UnexpectedPacket,
32    InvalidLFSOid,
33    InvalidInteger,
34    ParseError,
35    UnknownCommand,
36    MissingData,
37    ExtraData,
38    CorruptData,
39    NotAllowed,
40    InvalidPath,
41    DowncastError,
42}
43
44#[derive(Debug)]
45pub struct Error {
46    kind: ErrorKind,
47    internal: Option<Box<error::Error + Send + Sync>>,
48    message: Option<String>,
49}
50
51impl fmt::Display for Error {
52    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
53        match self.kind {
54            // Despite the text, this is not a fatal error in our sense. For compatibility with
55            // Git, however, we choose to preserve the same wording.
56            ErrorKind::NoSuchRevision => write!(f, "fatal: needed a single revision"),
57            ErrorKind::Conflict => write!(f, "fatal: conflict"),
58            ErrorKind::PCREError => match self.internal {
59                Some(ref e) => write!(f, "fatal: invalid regular expression: {}", e),
60                None => write!(f, "fatal: invalid regular expression"),
61            },
62            ErrorKind::IOError => match self.internal {
63                Some(ref e) => write!(f, "fatal: I/O error: {}", e),
64                None => write!(f, "fatal: unknown I/O error"),
65            },
66            ErrorKind::GitError => match self.internal {
67                Some(ref e) => write!(f, "fatal: {}", e),
68                None => write!(f, "fatal: an unknown error occurred"),
69            },
70            ErrorKind::BadPktlineHeader => write!(f, "malformed or unknown pkt-line header"),
71            ErrorKind::InvalidPacket => write!(f, "invalid or malformed packet"),
72            ErrorKind::UnexpectedPacket => write!(f, "unexpected packet while parsing"),
73            ErrorKind::InvalidLFSOid => write!(f, "invalid or malformed LFS oid"),
74            ErrorKind::InvalidInteger => write!(f, "invalid or malformed integer or size value"),
75            ErrorKind::ParseError => write!(f, "parse error"),
76            ErrorKind::UnknownCommand => write!(f, "unknown command or operation"),
77            ErrorKind::MissingData => write!(f, "incomplete or missing data"),
78            ErrorKind::ExtraData => write!(f, "extra data"),
79            ErrorKind::CorruptData => write!(f, "corrupt data"),
80            ErrorKind::NotAllowed => write!(f, "not allowed"),
81            ErrorKind::InvalidPath => write!(f, "invalid path"),
82            ErrorKind::DowncastError => write!(f, "unexpected type when downcasting"),
83        }?;
84        if let Some(ref msg) = self.message {
85            write!(f, ": {}", msg)?;
86        };
87        Ok(())
88    }
89}
90
91impl error::Error for Error {
92    fn description(&self) -> &str {
93        "an unknown error"
94    }
95}
96
97impl PartialEq for Error {
98    fn eq(&self, other: &Self) -> bool {
99        self.kind == other.kind
100    }
101}
102
103impl Error {
104    /// Create a new error.
105    ///
106    /// If this error was caused by another error, specify it as `Some(error)`.
107    pub fn new<E: Into<Box<error::Error + Send + Sync>>>(
108        kind: ErrorKind,
109        error: Option<E>,
110    ) -> Self {
111        Error {
112            kind,
113            internal: error.map(|e| e.into()),
114            message: None,
115        }
116    }
117
118    /// Create a new error without wrapping any other error.
119    pub fn new_simple(kind: ErrorKind) -> Self {
120        Error {
121            kind,
122            internal: None,
123            message: None,
124        }
125    }
126
127    /// Create a new error without wrapping any other error.
128    pub fn from_message<M: Into<String>>(kind: ErrorKind, msg: M) -> Self {
129        Error {
130            kind,
131            internal: None,
132            message: Some(msg.into()),
133        }
134    }
135
136    /// Return the kind of this error.
137    pub fn kind(&self) -> ErrorKind {
138        self.kind
139    }
140
141    /// Return the kind of this error when converted into an `io::Error`.
142    ///
143    /// If the internal error is an `io::Error`, returns its kind; otherwise, returns the kind it
144    /// would have if it were converted into an `io::Error`.
145    pub fn io_kind(&self) -> io::ErrorKind {
146        match self.internal {
147            Some(ref e) => match e.downcast_ref::<io::Error>() {
148                Some(x) => x.kind(),
149                None => io::ErrorKind::InvalidData,
150            },
151            None => io::ErrorKind::InvalidData,
152        }
153    }
154
155    /// Indicate whether this error is considered fatal.
156    ///
157    /// An error is fatal if it results in an exit of 2 or higher. A missing revision is not
158    /// considered fatal, but other errors are.
159    pub fn fatal(&self) -> bool {
160        self.exit_status() == ExitStatus::Fatal
161    }
162
163    /// Return the exit status for this error.
164    pub fn exit_status(&self) -> ExitStatus {
165        match self.kind {
166            ErrorKind::NoSuchRevision => ExitStatus::NonFatal,
167            ErrorKind::Conflict => ExitStatus::NonFatal,
168            _ => ExitStatus::Fatal,
169        }
170    }
171}
172
173impl convert::From<Error> for io::Error {
174    fn from(error: Error) -> io::Error {
175        io::Error::new(error.io_kind(), error)
176    }
177}
178
179impl convert::From<git2::Error> for Error {
180    fn from(error: git2::Error) -> Self {
181        let kind = match error.code() {
182            git2::ErrorCode::NotFound => ErrorKind::NoSuchRevision,
183            git2::ErrorCode::Conflict => ErrorKind::Conflict,
184            _ => ErrorKind::GitError,
185        };
186        Error::new(kind, Some(error))
187    }
188}
189
190#[cfg(feature = "pcre")]
191impl convert::From<pcre2::Error> for Error {
192    fn from(error: pcre2::Error) -> Self {
193        Error::new(ErrorKind::PCREError, Some(error))
194    }
195}
196
197impl convert::From<io::Error> for Error {
198    fn from(error: io::Error) -> Self {
199        Error::new(ErrorKind::IOError, Some(error))
200    }
201}