#![warn(missing_docs)]
mod category;
#[cfg(feature = "http")]
mod http;
#[cfg(feature = "json_rpc")]
mod json_rpc;
use std::fmt::Display;
pub use category::{Category, CategoryExt};
pub type Result<T> = std::result::Result<T, Error>;
pub struct Error {
anyhow: anyhow::Error,
category: Option<Category>,
}
impl Error {
pub fn into_anyhow(self) -> anyhow::Error {
self.anyhow
}
pub fn category(&self) -> Option<&Category> {
self.category.as_ref()
}
pub fn context<C>(mut self, context: C) -> Self
where
C: Display + Send + Sync + 'static,
{
self.anyhow = self.anyhow.context(context);
self
}
#[cfg(feature = "http")]
pub fn http_status(&self) -> u16 {
http::status_code(self.category.as_ref())
}
#[cfg(feature = "json_rpc")]
pub fn json_rpc_status(&self) -> i32 {
json_rpc::status_code(self.category.as_ref())
}
}
impl<E> From<E> for Error
where
anyhow::Error: From<E>,
{
fn from(e: E) -> Self {
Error {
anyhow: anyhow::Error::from(e),
category: None,
}
}
}
pub trait Context<T, E> {
fn context<C>(self, context: C) -> Result<T>
where
C: Display + Send + Sync + 'static;
fn with_context<C, F>(self, f: F) -> Result<T>
where
C: Display + Send + Sync + 'static,
F: FnOnce() -> C;
}
impl<T, E> Context<T, E> for std::result::Result<T, E>
where
E: Into<Error>,
{
fn context<C>(self, context: C) -> Result<T>
where
C: Display + Send + Sync + 'static,
{
match self {
Ok(t) => Ok(t),
Err(e) => Err(e.into().context(context)),
}
}
fn with_context<C, F>(self, f: F) -> Result<T>
where
C: Display + Send + Sync + 'static,
F: FnOnce() -> C,
{
match self {
Ok(t) => Ok(t),
Err(e) => Err(e.into().context(f())),
}
}
}
impl<T> Context<T, std::convert::Infallible> for Option<T> {
fn context<C>(self, context: C) -> Result<T>
where
C: Display + Send + Sync + 'static,
{
match self {
Some(t) => Ok(t),
None => Err(anyhow::anyhow!(context.to_string()).into()),
}
}
fn with_context<C, F>(self, f: F) -> Result<T>
where
C: Display + Send + Sync + 'static,
F: FnOnce() -> C,
{
match self {
Some(t) => Ok(t),
None => Err(anyhow::anyhow!(f().to_string()).into()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn returns_with_question_mark() {
fn _api() -> super::Result<()> {
"foo".parse::<usize>()?;
Ok(())
}
}
#[test]
fn attaches_cause() {
let err = "foo".parse::<usize>().bad_request();
assert_eq!(err.unwrap_err().category(), Some(&Category::BadRequest));
}
#[test]
fn can_attach_context() {
let _ = "foo".parse::<usize>().bad_request().context("Some context");
}
}