// This file is part of Moonfire NVR, a security camera network video recorder. // Copyright (C) 2018 Scott Lamb // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations including // the two. // // You must obey the GNU General Public License in all respects for all // of the code used other than OpenSSL. If you modify file(s) with this // exception, you may extend this exception to your version of the // file(s), but you are not obligated to do so. If you do not wish to do // so, delete this exception statement from your version. If you delete // this exception statement from all source files in the program, then // also delete it here. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . use failure::{Backtrace, Context, Fail}; use std::fmt; #[derive(Debug)] pub struct Error { inner: Context, } impl Error { pub fn kind(&self) -> ErrorKind { *self.inner.get_context() } } impl Fail for Error { fn cause(&self) -> Option<&Fail> { self.inner.cause() } fn backtrace(&self) -> Option<&Backtrace> { self.inner.backtrace() } } impl From for Error { fn from(kind: ErrorKind) -> Error { Error { inner: Context::new(kind) } } } impl From> for Error { fn from(inner: Context) -> Error { Error { inner } } } /*impl From for Error { fn from(e: failure::Error) -> Error { Error { inner: e.context(ErrorKind::Unknown) } } } impl From for Error { fn from(e: E) -> Error { let f = e as Fail; Error { inner: f.context(ErrorKind::Unknown) } } }*/ impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.inner.cause() { None => fmt::Display::fmt(&self.kind(), f), Some(c) => write!(f, "{}: {}", self.kind(), c), } } } /// Error kind. /// /// These codes are taken from /// [grpc::StatusCode](https://github.com/grpc/grpc/blob/0e00c430827e81d61e1e7164ef04ca21ccbfaa77/include/grpcpp/impl/codegen/status_code_enum.h), /// which is a nice general-purpose classification of errors. See that link for descriptions of /// each error. #[derive(Copy, Clone, Eq, PartialEq, Debug, Fail)] pub enum ErrorKind { #[fail(display = "Cancelled")] Cancelled, #[fail(display = "Unknown")] Unknown, #[fail(display = "Invalid argument")] InvalidArgument, #[fail(display = "Deadline exceeded")] DeadlineExceeded, #[fail(display = "Not found")] NotFound, #[fail(display = "Already exists")] AlreadyExists, #[fail(display = "Permission denied")] PermissionDenied, #[fail(display = "Unauthenticated")] Unauthenticated, #[fail(display = "Resource exhausted")] ResourceExhausted, #[fail(display = "Failed precondition")] FailedPrecondition, #[fail(display = "Aborted")] Aborted, #[fail(display = "Out of range")] OutOfRange, #[fail(display = "Unimplemented")] Unimplemented, #[fail(display = "Internal")] Internal, #[fail(display = "Unavailable")] Unavailable, #[fail(display = "Data loss")] DataLoss, #[doc(hidden)] #[fail(display = "__Nonexhaustive")] __Nonexhaustive, } /// Extension methods for `Result`. pub trait ResultExt { /// Annotates an error with the given kind. /// Example: /// ``` /// use moonfire_base::{ErrorKind, ResultExt}; /// use std::io::Read; /// let mut buf = [0u8; 1]; /// let r = std::io::Cursor::new("").read_exact(&mut buf[..]).err_kind(ErrorKind::Internal); /// assert_eq!(r.unwrap_err().kind(), ErrorKind::Internal); /// ``` fn err_kind(self, k: ErrorKind) -> Result; } impl ResultExt for Result where E: Into { fn err_kind(self, k: ErrorKind) -> Result { self.map_err(|e| e.into().context(k).into()) } } /// Like `failure::bail!`, but the first argument specifies a type as an `ErrorKind`. /// /// Example: /// ``` /// use moonfire_base::bail_t; /// let e = || -> Result<(), moonfire_base::Error> { /// bail_t!(Unauthenticated, "unknown user: {}", "slamb"); /// }().unwrap_err(); /// assert_eq!(e.kind(), moonfire_base::ErrorKind::Unauthenticated); /// assert_eq!(e.to_string(), "Unauthenticated: unknown user: slamb"); /// ``` #[macro_export] macro_rules! bail_t { ($t:ident, $e:expr) => { return Err(failure::err_msg($e).context($crate::ErrorKind::$t).into()); }; ($t:ident, $fmt:expr, $($arg:tt)+) => { return Err(failure::err_msg(format!($fmt, $($arg)+)).context($crate::ErrorKind::$t).into()); }; } /// Like `failure::format_err!`, but the first argument specifies a type as an `ErrorKind`. /// /// Example: /// ``` /// use moonfire_base::format_err_t; /// let e = format_err_t!(Unauthenticated, "unknown user: {}", "slamb"); /// assert_eq!(e.kind(), moonfire_base::ErrorKind::Unauthenticated); /// assert_eq!(e.to_string(), "Unauthenticated: unknown user: slamb"); /// ``` #[macro_export] macro_rules! format_err_t { ($t:ident, $e:expr) => { Into::<$crate::Error>::into(failure::err_msg($e).context($crate::ErrorKind::$t)) }; ($t:ident, $fmt:expr, $($arg:tt)+) => { Into::<$crate::Error>::into(failure::err_msg(format!($fmt, $($arg)+)) .context($crate::ErrorKind::$t)) }; }