use conjure_object::Value;
use parking_lot::Mutex;
use serde::Serialize;
use std::borrow::Cow;
use std::collections::hash_map::{self, HashMap};
use std::error;
use std::fmt;
use std::ops::Index;
use std::time::Duration;
use crate::{ErrorType, Internal, SerializableError};
#[derive(Debug)]
pub struct ThrottleError {
duration: Option<Duration>,
}
impl ThrottleError {
#[inline]
pub fn duration(&self) -> Option<Duration> {
self.duration
}
}
#[derive(Debug)]
pub struct UnavailableError(());
#[derive(Debug)]
pub enum ErrorKind {
Service(SerializableError),
Throttle(ThrottleError),
Unavailable(UnavailableError),
#[doc(hidden)]
__NonExhaustive,
}
#[derive(Debug)]
struct Inner {
cause: Box<dyn error::Error + Sync + Send>,
cause_safe: bool,
kind: ErrorKind,
safe_params: HashMap<Cow<'static, str>, Value>,
unsafe_params: HashMap<Cow<'static, str>, Value>,
backtraces: Vec<Backtrace>,
}
#[derive(Debug)]
pub struct Error(Box<Inner>);
impl Error {
pub fn service<E, T>(cause: E, error_type: T) -> Error
where
E: Into<Box<dyn error::Error + Sync + Send>>,
T: ErrorType + Serialize,
{
Error::service_inner(
cause.into(),
false,
crate::encode(&error_type),
error_type.safe_args(),
)
}
pub fn service_safe<E, T>(cause: E, error_type: T) -> Error
where
E: Into<Box<dyn error::Error + Sync + Send>>,
T: ErrorType + Serialize,
{
Error::service_inner(
cause.into(),
true,
crate::encode(&error_type),
error_type.safe_args(),
)
}
fn service_inner(
cause: Box<dyn error::Error + Sync + Send>,
cause_safe: bool,
error: SerializableError,
safe_args: &[&str],
) -> Error {
let mut safe_params = HashMap::new();
let mut unsafe_params = HashMap::new();
for (key, value) in error.parameters() {
let key = Cow::Owned(key.clone());
let value = Value::String(value.clone());
if safe_args.contains(&&*key) {
safe_params.insert(key, value);
} else {
unsafe_params.insert(key, value);
}
}
let mut error = Error::new(cause, cause_safe, ErrorKind::Service(error));
error.0.safe_params = safe_params;
error.0.unsafe_params = unsafe_params;
error
}
pub fn throttle<E>(cause: E) -> Error
where
E: Into<Box<dyn error::Error + Sync + Send>>,
{
Error::new(
cause.into(),
false,
ErrorKind::Throttle(ThrottleError { duration: None }),
)
}
pub fn throttle_safe<E>(cause: E) -> Error
where
E: Into<Box<dyn error::Error + Sync + Send>>,
{
Error::new(
cause.into(),
true,
ErrorKind::Throttle(ThrottleError { duration: None }),
)
}
pub fn throttle_for<E>(cause: E, duration: Duration) -> Error
where
E: Into<Box<dyn error::Error + Sync + Send>>,
{
Error::new(
cause.into(),
false,
ErrorKind::Throttle(ThrottleError {
duration: Some(duration),
}),
)
}
pub fn throttle_for_safe<E>(cause: E, duration: Duration) -> Error
where
E: Into<Box<dyn error::Error + Sync + Send>>,
{
Error::new(
cause.into(),
true,
ErrorKind::Throttle(ThrottleError {
duration: Some(duration),
}),
)
}
pub fn unavailable<E>(cause: E) -> Error
where
E: Into<Box<dyn error::Error + Sync + Send>>,
{
Error::new(
cause.into(),
false,
ErrorKind::Unavailable(UnavailableError(())),
)
}
pub fn unavailable_safe<E>(cause: E) -> Error
where
E: Into<Box<dyn error::Error + Sync + Send>>,
{
Error::new(
cause.into(),
true,
ErrorKind::Unavailable(UnavailableError(())),
)
}
pub fn internal<E>(cause: E) -> Error
where
E: Into<Box<dyn error::Error + Sync + Send>>,
{
Error::service(cause, Internal::new())
}
pub fn internal_safe<E>(cause: E) -> Error
where
E: Into<Box<dyn error::Error + Sync + Send>>,
{
Error::service_safe(cause, Internal::new())
}
fn new(cause: Box<dyn error::Error + Sync + Send>, cause_safe: bool, kind: ErrorKind) -> Error {
let inner = Inner {
cause,
cause_safe,
kind,
safe_params: HashMap::new(),
unsafe_params: HashMap::new(),
backtraces: vec![],
};
Error(Box::new(inner)).with_backtrace()
}
#[inline]
pub fn cause(&self) -> &(dyn error::Error + 'static + Sync + Send) {
&*self.0.cause
}
#[inline]
pub fn cause_safe(&self) -> bool {
self.0.cause_safe
}
#[inline]
pub fn kind(&self) -> &ErrorKind {
&self.0.kind
}
pub fn with_safe_param<T>(mut self, key: &'static str, value: T) -> Error
where
T: Serialize,
{
let value = serde_value::to_value(value).expect("value failed to serialize");
self.0.safe_params.insert(Cow::Borrowed(key), value);
self
}
pub fn with_unsafe_param<T>(mut self, key: &'static str, value: T) -> Error
where
T: Serialize,
{
let value = serde_value::to_value(value).expect("value failed to serialize");
self.0.unsafe_params.insert(Cow::Borrowed(key), value);
self
}
#[inline]
pub fn safe_params(&self) -> Params<'_> {
Params(&self.0.safe_params)
}
#[inline]
pub fn unsafe_params(&self) -> Params<'_> {
Params(&self.0.unsafe_params)
}
#[inline]
pub fn with_backtrace(mut self) -> Error {
self.0.backtraces.push(Backtrace::new());
self
}
#[inline]
pub fn backtraces(&self) -> &[Backtrace] {
&self.0.backtraces
}
}
#[derive(Debug)]
pub struct Params<'a>(&'a HashMap<Cow<'static, str>, Value>);
impl<'a> Params<'a> {
#[inline]
pub fn iter(&self) -> ParamsIter<'a> {
ParamsIter(self.0.iter())
}
#[inline]
pub fn len(&self) -> usize {
self.0.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
impl<'a> Index<&str> for Params<'a> {
type Output = Value;
#[inline]
fn index(&self, key: &str) -> &Value {
&self.0[key]
}
}
impl<'a> IntoIterator for &Params<'a> {
type IntoIter = ParamsIter<'a>;
type Item = (&'a str, &'a Value);
#[inline]
fn into_iter(self) -> ParamsIter<'a> {
self.iter()
}
}
pub struct ParamsIter<'a>(hash_map::Iter<'a, Cow<'static, str>, Value>);
impl<'a> Iterator for ParamsIter<'a> {
type Item = (&'a str, &'a Value);
#[inline]
fn next(&mut self) -> Option<(&'a str, &'a Value)> {
self.0.next().map(|(a, b)| (&**a, b))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
pub struct Backtrace(Mutex<backtrace::Backtrace>);
impl fmt::Debug for Backtrace {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut backtrace = self.0.lock();
backtrace.resolve();
fmt::Debug::fmt(&*backtrace, fmt)
}
}
impl Backtrace {
#[inline]
fn new() -> Backtrace {
Backtrace(Mutex::new(backtrace::Backtrace::new_unresolved()))
}
}