1use libc::c_int;
2use std::env::JoinPathsError;
3use std::error;
4use std::ffi::{CStr, CString, NulError};
5use std::fmt;
6use std::str;
7
8use crate::{raw, ErrorClass, ErrorCode};
9
10#[derive(Debug, PartialEq)]
12pub struct Error {
13 code: c_int,
14 klass: c_int,
15 message: Box<str>,
16}
17
18impl Error {
19 pub fn new<S: AsRef<str>>(code: ErrorCode, class: ErrorClass, message: S) -> Self {
25 let mut err = Error::from_str(message.as_ref());
26 err.set_code(code);
27 err.set_class(class);
28 err
29 }
30
31 pub fn last_error(code: c_int) -> Error {
36 crate::init();
37 unsafe {
38 let ptr = raw::git_error_last();
54 let err = if ptr.is_null() {
55 let mut error = Error::from_str("an unknown git error occurred");
56 error.code = code;
57 error
58 } else {
59 Error::from_raw(code, ptr)
60 };
61 raw::git_error_clear();
62 err
63 }
64 }
65
66 unsafe fn from_raw(code: c_int, ptr: *const raw::git_error) -> Error {
67 let message = CStr::from_ptr((*ptr).message as *const _).to_bytes();
68 let message = String::from_utf8_lossy(message).into_owned().into();
69 Error {
70 code,
71 klass: (*ptr).klass,
72 message,
73 }
74 }
75
76 pub fn from_str(s: &str) -> Error {
81 Error {
82 code: raw::GIT_ERROR as c_int,
83 klass: raw::GIT_ERROR_NONE as c_int,
84 message: s.into(),
85 }
86 }
87
88 pub fn code(&self) -> ErrorCode {
95 match self.raw_code() {
96 raw::GIT_OK => super::ErrorCode::GenericError,
97 raw::GIT_ERROR => super::ErrorCode::GenericError,
98 raw::GIT_ENOTFOUND => super::ErrorCode::NotFound,
99 raw::GIT_EEXISTS => super::ErrorCode::Exists,
100 raw::GIT_EAMBIGUOUS => super::ErrorCode::Ambiguous,
101 raw::GIT_EBUFS => super::ErrorCode::BufSize,
102 raw::GIT_EUSER => super::ErrorCode::User,
103 raw::GIT_EBAREREPO => super::ErrorCode::BareRepo,
104 raw::GIT_EUNBORNBRANCH => super::ErrorCode::UnbornBranch,
105 raw::GIT_EUNMERGED => super::ErrorCode::Unmerged,
106 raw::GIT_ENONFASTFORWARD => super::ErrorCode::NotFastForward,
107 raw::GIT_EINVALIDSPEC => super::ErrorCode::InvalidSpec,
108 raw::GIT_ECONFLICT => super::ErrorCode::Conflict,
109 raw::GIT_ELOCKED => super::ErrorCode::Locked,
110 raw::GIT_EMODIFIED => super::ErrorCode::Modified,
111 raw::GIT_PASSTHROUGH => super::ErrorCode::GenericError,
112 raw::GIT_ITEROVER => super::ErrorCode::GenericError,
113 raw::GIT_EAUTH => super::ErrorCode::Auth,
114 raw::GIT_ECERTIFICATE => super::ErrorCode::Certificate,
115 raw::GIT_EAPPLIED => super::ErrorCode::Applied,
116 raw::GIT_EPEEL => super::ErrorCode::Peel,
117 raw::GIT_EEOF => super::ErrorCode::Eof,
118 raw::GIT_EINVALID => super::ErrorCode::Invalid,
119 raw::GIT_EUNCOMMITTED => super::ErrorCode::Uncommitted,
120 raw::GIT_EDIRECTORY => super::ErrorCode::Directory,
121 raw::GIT_EMERGECONFLICT => super::ErrorCode::MergeConflict,
122 raw::GIT_EMISMATCH => super::ErrorCode::HashsumMismatch,
123 raw::GIT_EINDEXDIRTY => super::ErrorCode::IndexDirty,
124 raw::GIT_EAPPLYFAIL => super::ErrorCode::ApplyFail,
125 raw::GIT_EOWNER => super::ErrorCode::Owner,
126 raw::GIT_TIMEOUT => super::ErrorCode::Timeout,
127 _ => super::ErrorCode::GenericError,
128 }
129 }
130
131 pub fn set_code(&mut self, code: ErrorCode) {
136 self.code = match code {
137 ErrorCode::GenericError => raw::GIT_ERROR,
138 ErrorCode::NotFound => raw::GIT_ENOTFOUND,
139 ErrorCode::Exists => raw::GIT_EEXISTS,
140 ErrorCode::Ambiguous => raw::GIT_EAMBIGUOUS,
141 ErrorCode::BufSize => raw::GIT_EBUFS,
142 ErrorCode::User => raw::GIT_EUSER,
143 ErrorCode::BareRepo => raw::GIT_EBAREREPO,
144 ErrorCode::UnbornBranch => raw::GIT_EUNBORNBRANCH,
145 ErrorCode::Unmerged => raw::GIT_EUNMERGED,
146 ErrorCode::NotFastForward => raw::GIT_ENONFASTFORWARD,
147 ErrorCode::InvalidSpec => raw::GIT_EINVALIDSPEC,
148 ErrorCode::Conflict => raw::GIT_ECONFLICT,
149 ErrorCode::Locked => raw::GIT_ELOCKED,
150 ErrorCode::Modified => raw::GIT_EMODIFIED,
151 ErrorCode::Auth => raw::GIT_EAUTH,
152 ErrorCode::Certificate => raw::GIT_ECERTIFICATE,
153 ErrorCode::Applied => raw::GIT_EAPPLIED,
154 ErrorCode::Peel => raw::GIT_EPEEL,
155 ErrorCode::Eof => raw::GIT_EEOF,
156 ErrorCode::Invalid => raw::GIT_EINVALID,
157 ErrorCode::Uncommitted => raw::GIT_EUNCOMMITTED,
158 ErrorCode::Directory => raw::GIT_EDIRECTORY,
159 ErrorCode::MergeConflict => raw::GIT_EMERGECONFLICT,
160 ErrorCode::HashsumMismatch => raw::GIT_EMISMATCH,
161 ErrorCode::IndexDirty => raw::GIT_EINDEXDIRTY,
162 ErrorCode::ApplyFail => raw::GIT_EAPPLYFAIL,
163 ErrorCode::Owner => raw::GIT_EOWNER,
164 ErrorCode::Timeout => raw::GIT_TIMEOUT,
165 };
166 }
167
168 pub fn class(&self) -> ErrorClass {
174 match self.raw_class() {
175 raw::GIT_ERROR_NONE => super::ErrorClass::None,
176 raw::GIT_ERROR_NOMEMORY => super::ErrorClass::NoMemory,
177 raw::GIT_ERROR_OS => super::ErrorClass::Os,
178 raw::GIT_ERROR_INVALID => super::ErrorClass::Invalid,
179 raw::GIT_ERROR_REFERENCE => super::ErrorClass::Reference,
180 raw::GIT_ERROR_ZLIB => super::ErrorClass::Zlib,
181 raw::GIT_ERROR_REPOSITORY => super::ErrorClass::Repository,
182 raw::GIT_ERROR_CONFIG => super::ErrorClass::Config,
183 raw::GIT_ERROR_REGEX => super::ErrorClass::Regex,
184 raw::GIT_ERROR_ODB => super::ErrorClass::Odb,
185 raw::GIT_ERROR_INDEX => super::ErrorClass::Index,
186 raw::GIT_ERROR_OBJECT => super::ErrorClass::Object,
187 raw::GIT_ERROR_NET => super::ErrorClass::Net,
188 raw::GIT_ERROR_TAG => super::ErrorClass::Tag,
189 raw::GIT_ERROR_TREE => super::ErrorClass::Tree,
190 raw::GIT_ERROR_INDEXER => super::ErrorClass::Indexer,
191 raw::GIT_ERROR_SSL => super::ErrorClass::Ssl,
192 raw::GIT_ERROR_SUBMODULE => super::ErrorClass::Submodule,
193 raw::GIT_ERROR_THREAD => super::ErrorClass::Thread,
194 raw::GIT_ERROR_STASH => super::ErrorClass::Stash,
195 raw::GIT_ERROR_CHECKOUT => super::ErrorClass::Checkout,
196 raw::GIT_ERROR_FETCHHEAD => super::ErrorClass::FetchHead,
197 raw::GIT_ERROR_MERGE => super::ErrorClass::Merge,
198 raw::GIT_ERROR_SSH => super::ErrorClass::Ssh,
199 raw::GIT_ERROR_FILTER => super::ErrorClass::Filter,
200 raw::GIT_ERROR_REVERT => super::ErrorClass::Revert,
201 raw::GIT_ERROR_CALLBACK => super::ErrorClass::Callback,
202 raw::GIT_ERROR_CHERRYPICK => super::ErrorClass::CherryPick,
203 raw::GIT_ERROR_DESCRIBE => super::ErrorClass::Describe,
204 raw::GIT_ERROR_REBASE => super::ErrorClass::Rebase,
205 raw::GIT_ERROR_FILESYSTEM => super::ErrorClass::Filesystem,
206 raw::GIT_ERROR_PATCH => super::ErrorClass::Patch,
207 raw::GIT_ERROR_WORKTREE => super::ErrorClass::Worktree,
208 raw::GIT_ERROR_SHA1 => super::ErrorClass::Sha1,
209 raw::GIT_ERROR_HTTP => super::ErrorClass::Http,
210 _ => super::ErrorClass::None,
211 }
212 }
213
214 pub fn set_class(&mut self, class: ErrorClass) {
219 self.klass = match class {
220 ErrorClass::None => raw::GIT_ERROR_NONE,
221 ErrorClass::NoMemory => raw::GIT_ERROR_NOMEMORY,
222 ErrorClass::Os => raw::GIT_ERROR_OS,
223 ErrorClass::Invalid => raw::GIT_ERROR_INVALID,
224 ErrorClass::Reference => raw::GIT_ERROR_REFERENCE,
225 ErrorClass::Zlib => raw::GIT_ERROR_ZLIB,
226 ErrorClass::Repository => raw::GIT_ERROR_REPOSITORY,
227 ErrorClass::Config => raw::GIT_ERROR_CONFIG,
228 ErrorClass::Regex => raw::GIT_ERROR_REGEX,
229 ErrorClass::Odb => raw::GIT_ERROR_ODB,
230 ErrorClass::Index => raw::GIT_ERROR_INDEX,
231 ErrorClass::Object => raw::GIT_ERROR_OBJECT,
232 ErrorClass::Net => raw::GIT_ERROR_NET,
233 ErrorClass::Tag => raw::GIT_ERROR_TAG,
234 ErrorClass::Tree => raw::GIT_ERROR_TREE,
235 ErrorClass::Indexer => raw::GIT_ERROR_INDEXER,
236 ErrorClass::Ssl => raw::GIT_ERROR_SSL,
237 ErrorClass::Submodule => raw::GIT_ERROR_SUBMODULE,
238 ErrorClass::Thread => raw::GIT_ERROR_THREAD,
239 ErrorClass::Stash => raw::GIT_ERROR_STASH,
240 ErrorClass::Checkout => raw::GIT_ERROR_CHECKOUT,
241 ErrorClass::FetchHead => raw::GIT_ERROR_FETCHHEAD,
242 ErrorClass::Merge => raw::GIT_ERROR_MERGE,
243 ErrorClass::Ssh => raw::GIT_ERROR_SSH,
244 ErrorClass::Filter => raw::GIT_ERROR_FILTER,
245 ErrorClass::Revert => raw::GIT_ERROR_REVERT,
246 ErrorClass::Callback => raw::GIT_ERROR_CALLBACK,
247 ErrorClass::CherryPick => raw::GIT_ERROR_CHERRYPICK,
248 ErrorClass::Describe => raw::GIT_ERROR_DESCRIBE,
249 ErrorClass::Rebase => raw::GIT_ERROR_REBASE,
250 ErrorClass::Filesystem => raw::GIT_ERROR_FILESYSTEM,
251 ErrorClass::Patch => raw::GIT_ERROR_PATCH,
252 ErrorClass::Worktree => raw::GIT_ERROR_WORKTREE,
253 ErrorClass::Sha1 => raw::GIT_ERROR_SHA1,
254 ErrorClass::Http => raw::GIT_ERROR_HTTP,
255 } as c_int;
256 }
257
258 pub fn raw_code(&self) -> raw::git_error_code {
260 macro_rules! check( ($($e:ident,)*) => (
261 $(if self.code == raw::$e as c_int { raw::$e }) else *
262 else {
263 raw::GIT_ERROR
264 }
265 ) );
266 check!(
267 GIT_OK,
268 GIT_ERROR,
269 GIT_ENOTFOUND,
270 GIT_EEXISTS,
271 GIT_EAMBIGUOUS,
272 GIT_EBUFS,
273 GIT_EUSER,
274 GIT_EBAREREPO,
275 GIT_EUNBORNBRANCH,
276 GIT_EUNMERGED,
277 GIT_ENONFASTFORWARD,
278 GIT_EINVALIDSPEC,
279 GIT_ECONFLICT,
280 GIT_ELOCKED,
281 GIT_EMODIFIED,
282 GIT_EAUTH,
283 GIT_ECERTIFICATE,
284 GIT_EAPPLIED,
285 GIT_EPEEL,
286 GIT_EEOF,
287 GIT_EINVALID,
288 GIT_EUNCOMMITTED,
289 GIT_EDIRECTORY,
290 GIT_EMERGECONFLICT,
291 GIT_PASSTHROUGH,
292 GIT_ITEROVER,
293 GIT_RETRY,
294 GIT_EMISMATCH,
295 GIT_EINDEXDIRTY,
296 GIT_EAPPLYFAIL,
297 GIT_EOWNER,
298 GIT_TIMEOUT,
299 GIT_EUNCHANGED,
300 GIT_ENOTSUPPORTED,
301 GIT_EREADONLY,
302 )
303 }
304
305 pub fn raw_class(&self) -> raw::git_error_t {
307 macro_rules! check( ($($e:ident,)*) => (
308 $(if self.klass == raw::$e as c_int { raw::$e }) else *
309 else {
310 raw::GIT_ERROR_NONE
311 }
312 ) );
313 check!(
314 GIT_ERROR_NONE,
315 GIT_ERROR_NOMEMORY,
316 GIT_ERROR_OS,
317 GIT_ERROR_INVALID,
318 GIT_ERROR_REFERENCE,
319 GIT_ERROR_ZLIB,
320 GIT_ERROR_REPOSITORY,
321 GIT_ERROR_CONFIG,
322 GIT_ERROR_REGEX,
323 GIT_ERROR_ODB,
324 GIT_ERROR_INDEX,
325 GIT_ERROR_OBJECT,
326 GIT_ERROR_NET,
327 GIT_ERROR_TAG,
328 GIT_ERROR_TREE,
329 GIT_ERROR_INDEXER,
330 GIT_ERROR_SSL,
331 GIT_ERROR_SUBMODULE,
332 GIT_ERROR_THREAD,
333 GIT_ERROR_STASH,
334 GIT_ERROR_CHECKOUT,
335 GIT_ERROR_FETCHHEAD,
336 GIT_ERROR_MERGE,
337 GIT_ERROR_SSH,
338 GIT_ERROR_FILTER,
339 GIT_ERROR_REVERT,
340 GIT_ERROR_CALLBACK,
341 GIT_ERROR_CHERRYPICK,
342 GIT_ERROR_DESCRIBE,
343 GIT_ERROR_REBASE,
344 GIT_ERROR_FILESYSTEM,
345 GIT_ERROR_PATCH,
346 GIT_ERROR_WORKTREE,
347 GIT_ERROR_SHA1,
348 GIT_ERROR_HTTP,
349 )
350 }
351
352 pub fn message(&self) -> &str {
354 &self.message
355 }
356
357 pub(crate) unsafe fn raw_set_git_error(&self) -> raw::git_error_code {
363 let s = CString::new(self.message()).unwrap();
364 raw::git_error_set_str(self.class() as c_int, s.as_ptr());
365 self.raw_code()
366 }
367}
368
369impl error::Error for Error {}
370
371impl fmt::Display for Error {
372 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
373 write!(f, "{}", self.message)?;
374 match self.class() {
375 ErrorClass::None => {}
376 other => write!(f, "; class={:?} ({})", other, self.klass)?,
377 }
378 match self.code() {
379 ErrorCode::GenericError => {}
380 other => write!(f, "; code={:?} ({})", other, self.code)?,
381 }
382 Ok(())
383 }
384}
385
386impl From<NulError> for Error {
387 fn from(_: NulError) -> Error {
388 Error::from_str(
389 "data contained a nul byte that could not be \
390 represented as a string",
391 )
392 }
393}
394
395impl From<JoinPathsError> for Error {
396 fn from(e: JoinPathsError) -> Error {
397 Error::from_str(&e.to_string())
398 }
399}
400
401#[cfg(test)]
402mod tests {
403 use crate::{ErrorClass, ErrorCode};
404
405 #[test]
406 fn smoke() {
407 let (_td, repo) = crate::test::repo_init();
408
409 let err = repo.find_submodule("does_not_exist").err().unwrap();
410 assert_eq!(err.code(), ErrorCode::NotFound);
411 assert_eq!(err.class(), ErrorClass::Submodule);
412 }
413}