use core::{convert::Infallible, error::Error as _, fmt::Debug};
use crate::{
Error,
render::{DebugSourceChain, DisplayAsDebug},
};
pub trait State: Debug + Send + Sync + 'static {
type Repr: Debug + Send + Sync + 'static;
fn into_repr(self) -> Self::Repr
where
Self: Sized;
fn from_repr(state: Self::Repr) -> Self
where
Self: Sized;
fn from_repr_ref(state: &Self::Repr) -> &Self
where
Self: Sized;
}
impl<T> State for T
where
T: Debug + Send + Sync + 'static,
{
type Repr = T;
fn into_repr(self) -> Self::Repr {
self
}
fn from_repr(this: Self::Repr) -> Self
where
Self: Sized,
{
this
}
fn from_repr_ref(this: &Self::Repr) -> &Self
where
Self: Sized,
{
this
}
}
#[derive(Debug)]
pub struct Stateless(#[allow(unused)] [()]);
impl State for Stateless {
type Repr = Infallible;
}
pub struct Vacant<S>(Option<Error<S>>)
where
S: State;
impl<S> Vacant<S>
where
S: State,
{
pub(crate) fn new(err: Option<Error<S>>) -> Self {
Self(err)
}
pub fn with_state(self, state: S) -> Error<S> {
let Some(mut err) = self.0 else {
return Error::from_state(state);
};
err.0
.try_set_state(State::into_repr(state))
.expect("Vacant must be created with correct state storage type");
err
}
pub fn try_into_stateless(self) -> Option<Error> {
self.0.map(|s| {
s.try_into_stateless()
.expect("Vacant must not be created with an empty Error")
})
}
}
impl<S> Debug for Vacant<S>
where
S: State,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut ds = f.debug_struct("Vacant");
if let Some(err) = &self.0 {
if let Some(context) = err.context() {
ds.field("context", &DisplayAsDebug(context));
}
if let Some(payload) = err.payload() {
ds.field("payload", &DisplayAsDebug(payload));
}
if let Some(source) = err.erase_ref().source() {
ds.field("source", &DebugSourceChain(&source));
}
}
ds.finish()
}
}