pub type Result = std::result::Result; pub trait ResultExt { fn with_context(self, context: F) -> Result where C: Into, F: FnOnce() -> C; } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Code { InvalidArgument, FailedPrecondition, NotFound, PermissionDenied, Internal, } #[derive(Debug)] pub struct Status { code: Code, message: String, source: Option>, } impl Status { pub fn new(code: Code, message: impl Into) -> Self { Self { code, message: message.into(), source: None, } } pub fn not_found(message: impl Into) -> Self { Self::new(Code::NotFound, message) } pub fn invalid_argument(message: impl Into) -> Self { Self::new(Code::InvalidArgument, message) } pub fn failed_precondition(message: impl Into) -> Self { Self::new(Code::FailedPrecondition, message) } pub fn permission_denied(message: impl Into) -> Self { Self::new(Code::PermissionDenied, message) } pub fn internal(message: impl Into) -> Self { Self::new(Code::Internal, message) } pub fn with_source(mut self, source: E) -> Self where E: std::error::Error + Send + Sync + 'static, { self.source = Some(Box::new(source)); self } pub fn with_context(self, context: impl Into) -> Self { Self::new(self.code, context).with_source(self) } pub fn code(&self) -> Code { self.code } pub fn message(&self) -> &str { &self.message } } impl std::fmt::Display for Status { fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(formatter, "{:?}: {}", self.code, self.message) } } impl std::error::Error for Status { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { self.source .as_deref() .map(|source| source as &(dyn std::error::Error + 'static)) } } impl From for Status { fn from(error: std::io::Error) -> Self { let code = match error.kind() { std::io::ErrorKind::NotFound => Code::NotFound, std::io::ErrorKind::PermissionDenied => Code::PermissionDenied, _ => Code::Internal, }; Self::new(code, error.to_string()).with_source(error) } } impl ResultExt for std::result::Result where E: Into, { fn with_context(self, context: F) -> Result where C: Into, F: FnOnce() -> C, { self.map_err(|error| error.into().with_context(context())) } }