use std::error;
use std::fmt;
use crate::AdhocContext;
use crate::Chain;
use crate::Context;
use crate::InternalStatus;
use crate::Kind;
use crate::StdError;
use crate::StrictError;
use crate::Unkind;
#[derive(Debug)]
pub struct Status<K: Kind = Unkind, C: Context = AdhocContext> {
pub(crate) inner: Box<StatusDetails<K, C>>,
}
#[derive(Debug)]
pub(crate) struct StatusDetails<K: Kind, C: Context> {
pub(crate) kind: K,
pub(crate) source: Source,
pub(crate) data: C,
}
impl<K: Kind, C: Context> Status<K, C> {
pub fn new<U>(kind: U) -> Self
where
U: Into<K>,
{
Self {
inner: Box::new(StatusDetails {
kind: kind.into(),
source: Source::Empty,
data: Default::default(),
}),
}
}
#[cfg(feature = "send_sync")]
pub fn with_source<E>(mut self, error: E) -> Self
where
E: error::Error + Send + Sync + 'static,
{
self.inner.source = Source::Public(Box::new(error));
self
}
#[cfg(not(feature = "send_sync"))]
pub fn with_source<E>(mut self, error: E) -> Self
where
E: error::Error + 'static,
{
self.inner.source = Source::Public(Box::new(error));
self
}
#[cfg(feature = "send_sync")]
pub fn with_internal<E>(mut self, error: E) -> Self
where
E: error::Error + Send + Sync + 'static,
{
self.inner.source = Source::Private(Box::new(error));
self
}
#[cfg(not(feature = "send_sync"))]
pub fn with_internal<E>(mut self, error: E) -> Self
where
E: error::Error + 'static,
{
self.inner.source = Source::Private(Box::new(error));
self
}
pub fn context_with<F>(mut self, context: F) -> Self
where
F: Fn(C) -> C,
{
let mut data: C = Default::default();
std::mem::swap(&mut data, &mut self.inner.data);
let mut data = context(data);
std::mem::swap(&mut data, &mut self.inner.data);
self
}
pub fn context(&self) -> &C {
&self.inner.data
}
pub fn kind(&self) -> K {
self.inner.kind
}
pub fn sources(&self) -> Chain {
Chain::new(error::Error::source(self))
}
pub fn root_source(&self) -> Option<&StdError> {
self.sources().last()
}
pub fn into_internal(self) -> InternalStatus<K, C> {
InternalStatus::new(self)
}
pub fn into_err<T>(self) -> Result<T, Self> {
Err(self)
}
}
impl<K: Kind, C: Context> fmt::Display for Status<K, C> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "{}", self.inner.kind)?;
if !self.inner.data.is_empty() {
writeln!(f)?;
writeln!(f, "{}", self.inner.data)?;
}
Ok(())
}
}
impl<K: Kind, C: Context> std::ops::Deref for Status<K, C> {
type Target = C;
fn deref(&self) -> &Self::Target {
&self.inner.data
}
}
impl<K: Kind, C: Context> std::ops::DerefMut for Status<K, C> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner.data
}
}
impl<K: Kind, C: Context> error::Error for Status<K, C> {
fn cause(&self) -> Option<&dyn error::Error> {
self.inner.source.public()
}
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
self.inner.source.public()
}
}
#[derive(Debug)]
pub(crate) enum Source {
Public(Box<StrictError>),
Private(Box<StrictError>),
Empty,
}
impl Source {
pub(crate) fn public(&self) -> Option<&StdError> {
match self {
Self::Public(e) => Some(e.as_ref()),
_ => None,
}
}
pub(crate) fn any(&self) -> Option<&StdError> {
match self {
Self::Public(e) | Self::Private(e) => Some(e.as_ref()),
_ => None,
}
}
}
#[cfg(test)]
mod test {
use super::*;
use static_assertions::*;
#[test]
fn source() {
assert_impl_all!(Source: fmt::Debug);
#[cfg(feature = "send_sync")]
assert_impl_all!(Source: Send, Sync);
}
#[test]
fn status() {
assert_impl_all!(Status: fmt::Debug, fmt::Display, error::Error);
#[cfg(feature = "send_sync")]
assert_impl_all!(Status: Send, Sync);
}
}