use crate::{
context::{self, Literal},
nae::Nae,
payload,
ptr::{Align4, Align4Own, Align4PtrCompat, Align4Ref, Metadata, Mut, Ref},
};
use std::{
self,
any::TypeId,
error,
fmt::{self, Debug, Display},
mem::{self, ManuallyDrop, MaybeUninit},
ptr::NonNull,
};
#[repr(C)]
pub union RawError<S>
where
S: 'static,
{
const_body: ManuallyDrop<Align4Ref<'static, ConstBody>>,
boxed_body: ManuallyDrop<Align4Own<DynBody<S>>>,
inline_body: ManuallyDrop<Align4PtrCompat<S>>,
}
enum SelectRef<'a, S>
where
S: 'static,
{
Const(&'a Align4Ref<'static, ConstBody>),
Boxed(&'a Align4Own<DynBody<S>>),
Inline(&'a Align4PtrCompat<S>),
}
enum SelectMut<'a, S>
where
S: 'static,
{
Const(&'a mut Align4Ref<'static, ConstBody>),
Boxed(&'a mut Align4Own<DynBody<S>>),
Inline(&'a mut Align4PtrCompat<S>),
}
enum SelectOwn<S>
where
S: 'static,
{
Const(Align4Ref<'static, ConstBody>),
Boxed(Align4Own<DynBody<S>>),
Inline(Align4PtrCompat<S>),
}
impl<S> RawError<S> {
const KIND_CONST: Metadata = Metadata::_0;
const KIND_BOXED: Metadata = Metadata::_1;
const KIND_INLINE: Metadata = Metadata::_2;
fn kind(&self) -> Metadata {
unsafe { Metadata((&raw const (*self) as *const u8).read() & Metadata::MASK) }
}
fn select_ref(&self) -> SelectRef<'_, S> {
unsafe {
match self.kind() {
Self::KIND_CONST => SelectRef::Const(&self.const_body),
Self::KIND_BOXED => SelectRef::Boxed(&self.boxed_body),
Self::KIND_INLINE => SelectRef::Inline(&self.inline_body),
_ => unreachable!(),
}
}
}
fn select_mut(&mut self) -> SelectMut<'_, S> {
unsafe {
match self.kind() {
Self::KIND_CONST => SelectMut::Const(&mut self.const_body),
Self::KIND_BOXED => SelectMut::Boxed(&mut self.boxed_body),
Self::KIND_INLINE => SelectMut::Inline(&mut self.inline_body),
_ => unreachable!(),
}
}
}
fn select_own(self) -> SelectOwn<S> {
let kind = self.kind();
let mut this = ManuallyDrop::new(self);
unsafe {
match kind {
Self::KIND_CONST => SelectOwn::Const(ManuallyDrop::take(&mut this.const_body)),
Self::KIND_BOXED => SelectOwn::Boxed(ManuallyDrop::take(&mut this.boxed_body)),
Self::KIND_INLINE => SelectOwn::Inline(ManuallyDrop::take(&mut this.inline_body)),
_ => unreachable!(),
}
}
}
}
impl RawError<()> {
pub fn new_const<L>() -> Self
where
L: Literal + ?Sized,
{
let body: &'static Align4<ConstBody> = &const {
Align4(ConstBody {
context: L::LITERAL,
})
};
Self {
const_body: ManuallyDrop::new(Align4Ref::new(body, Self::KIND_CONST)),
}
}
}
impl<S> RawError<S> {
pub fn new_inline(state: S) -> Self {
Self {
inline_body: ManuallyDrop::new(Align4PtrCompat {
meta: Self::KIND_INLINE.0,
value: state,
}),
}
}
pub fn new_boxed<E, P, L>(state: S, source: E, payload: P) -> Self
where
E: error::Error + Send + Sync + 'static,
P: Display + Send + Sync + 'static,
L: Literal + context::Context + ?Sized,
{
let ptr = unsafe {
Align4Own::from_boxed(
Box::new(Align4(DynBody::<S, E, P, L::Repr> {
state,
vtable: &const { DynBodyVTable::new::<E, P, L::Repr>() },
source,
store: Store {
payload,
context: L::new_context(),
},
})),
Self::KIND_BOXED,
)
.cast::<DynBody<S>>()
};
Self {
boxed_body: ManuallyDrop::new(ptr),
}
}
pub fn context(&self) -> Option<&'_ (dyn Display + Send + Sync + 'static)> {
match self.select_ref() {
SelectRef::Const(body) => unsafe {
Some(
body.borrow()
.project(|body| &raw const (*body).context)
.deref(),
)
},
SelectRef::Boxed(body) => unsafe {
let vtable = body
.borrow()
.project(|body| &raw const (*body).vtable)
.copied();
(vtable.context)(body.borrow())
},
SelectRef::Inline(_body) => None,
}
}
pub fn payload(&self) -> Option<&'_ (dyn Display + Send + Sync + 'static)> {
match self.select_ref() {
SelectRef::Inline(_body) => None,
SelectRef::Const(_body) => None,
SelectRef::Boxed(body) => unsafe {
let vtable = body
.borrow()
.project(|body| &raw const (*body).vtable)
.copied();
(vtable.payload)(body.borrow())
},
}
}
pub fn source(&self) -> Option<&(dyn error::Error + Send + Sync + 'static)> {
match self.select_ref() {
SelectRef::Const(_body) => None,
SelectRef::Inline(_body) => None,
SelectRef::Boxed(body) => unsafe {
let vtable = body
.borrow()
.project(|body| &raw const (*body).vtable)
.copied();
(vtable.source)(body.borrow())
},
}
}
pub fn downcast_source_ref<E>(&self) -> Option<&E>
where
E: 'static,
{
match self.select_ref() {
SelectRef::Const(_body) => None,
SelectRef::Inline(_body) => None,
SelectRef::Boxed(body) => unsafe {
let vtable = body
.borrow()
.project(|body| &raw const (*body).vtable)
.copied();
(vtable.downcast_source_ref)(body.borrow(), TypeId::of::<E>())
.map(|err| err.cast::<E>().deref())
},
}
}
pub fn downcast_payload_ref<P>(&self) -> Option<&P>
where
P: 'static,
{
match self.select_ref() {
SelectRef::Const(_body) => None,
SelectRef::Inline(_body) => None,
SelectRef::Boxed(body) => unsafe {
let vtable = body
.borrow()
.project(|body| &raw const (*body).vtable)
.copied();
(vtable.downcast_payload_ref)(body.borrow(), TypeId::of::<P>())
.map(|err| err.cast::<P>().deref())
},
}
}
pub fn downcast_source_mut<E>(&mut self) -> Option<&mut E>
where
E: 'static,
{
match self.select_mut() {
SelectMut::Const(_body) => None,
SelectMut::Inline(_body) => None,
SelectMut::Boxed(body) => unsafe {
let vtable = body
.borrow()
.project(|body| &raw const (*body).vtable)
.copied();
(vtable.downcast_source_mut)(body.borrow_mut(), TypeId::of::<E>())
.map(|err| err.cast::<E>().deref_mut())
},
}
}
pub fn downcast_payload_mut<P>(&mut self) -> Option<&mut P>
where
P: 'static,
{
match self.select_mut() {
SelectMut::Const(_body) => None,
SelectMut::Inline(_body) => None,
SelectMut::Boxed(body) => unsafe {
let vtable = body
.borrow()
.project(|body| &raw const (*body).vtable)
.copied();
(vtable.downcast_payload_mut)(body.borrow_mut(), TypeId::of::<P>())
.map(|err| err.cast::<P>().deref_mut())
},
}
}
pub fn state(&self) -> &S {
match self.select_ref() {
SelectRef::Const(_body) => unsafe {
assert_eq!(TypeId::of::<S>(), TypeId::of::<()>());
&*(&() as *const _ as *const S)
},
SelectRef::Inline(_body) => unsafe {
&self.inline_body.value
},
SelectRef::Boxed(body) => unsafe {
body.borrow()
.project(|body| &raw const (*body).state)
.deref()
},
}
}
pub fn into_state(self) -> S {
match self.select_own() {
SelectOwn::Const(_body) => unsafe {
assert_eq!(TypeId::of::<S>(), TypeId::of::<()>());
#[allow(clippy::uninit_assumed_init)]
MaybeUninit::<S>::uninit().assume_init()
},
SelectOwn::Inline(body) => body.value,
SelectOwn::Boxed(body) => unsafe {
let vtable = body
.borrow()
.project(|body| &raw const (*body).vtable)
.copied();
(vtable.into_state)(body)
},
}
}
pub fn into_source(self) -> Option<Box<dyn error::Error + Send + Sync + 'static>> {
match self.select_own() {
SelectOwn::Const(_body) => None,
SelectOwn::Inline(_body) => None,
SelectOwn::Boxed(body) => unsafe {
let vtable = body
.borrow()
.project(|body| &raw const (*body).vtable)
.copied();
(vtable.into_source)(body)
},
}
}
pub fn into_parts<E, P>(self) -> (Option<E>, Option<P>, S)
where
E: 'static,
P: 'static,
{
match self.select_own() {
SelectOwn::Const(_body) => (None, None, unsafe {
assert_eq!(TypeId::of::<S>(), TypeId::of::<()>());
#[allow(clippy::uninit_assumed_init)]
MaybeUninit::<S>::uninit().assume_init()
}),
SelectOwn::Inline(body) => (None, None, body.value),
SelectOwn::Boxed(body) => unsafe {
let vtable = body
.borrow()
.project(|body| &raw const (*body).vtable)
.copied();
let mut err: Option<E> = None;
let mut payload: Option<P> = None;
let state = (vtable.into_parts)(
body,
TypeId::of::<E>(),
NonNull::new_unchecked(&raw mut err as *mut ()),
TypeId::of::<P>(),
NonNull::new_unchecked(&raw mut payload as *mut ()),
);
(err, payload, state)
},
}
}
pub fn chain(&self) -> impl Iterator<Item = &(dyn error::Error + 'static)> {
struct Chain<'a>(Option<&'a (dyn error::Error + 'static)>);
impl<'a> Iterator for Chain<'a> {
type Item = &'a (dyn error::Error + 'static);
fn next(&mut self) -> Option<Self::Item> {
let next = self.0.and_then(|err| err.source());
mem::replace(&mut self.0, next)
}
}
Chain(
self.source()
.map(|err| err as &(dyn error::Error + 'static)),
)
}
}
impl<S> Drop for RawError<S> {
fn drop(&mut self) {
match self.kind() {
Self::KIND_CONST => {}
Self::KIND_INLINE => unsafe {
ManuallyDrop::drop(&mut self.inline_body);
},
Self::KIND_BOXED => unsafe {
let vtable = self
.boxed_body
.borrow()
.project(|body| &raw const (*body).vtable)
.copied();
(vtable.into_state)(ManuallyDrop::take(&mut self.boxed_body));
},
_ => unreachable!(),
}
}
}
impl<S> Debug for RawError<S>
where
S: Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let state = format_args!("{:?}", self.state());
let mut segments = [
self.context().map(|s| s as &dyn Display),
(TypeId::of::<S>() != TypeId::of::<()>()).then_some(&state as _),
self.payload().map(|s| s as _),
self.source().map(|s| s as _),
]
.into_iter()
.flatten()
.peekable();
while let Some(segment) = segments.next() {
write!(f, "{}", segment)?;
if segments.peek().is_some() {
write!(f, ": ")?;
}
}
writeln!(f)?;
writeln!(f, "Caused by: ")?;
for err in self.chain() {
write!(f, " ")?;
writeln!(f, "{}", err)?;
}
Ok(())
}
}
impl<S> Display for RawError<S>
where
S: Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let state = format_args!("{:?}", self.state());
let mut segments = [
self.context().map(|s| s as &dyn Display),
(TypeId::of::<S>() != TypeId::of::<()>()).then_some(&state as _),
self.payload().map(|s| s as _),
self.source().map(|s| s as _),
]
.into_iter()
.flatten()
.peekable();
while let Some(segment) = segments.next() {
write!(f, "{}", segment)?;
if segments.peek().is_some() {
write!(f, ": ")?;
}
}
Ok(())
}
}
impl<S> error::Error for RawError<S>
where
S: Debug,
{
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
self.source()
.map(|err| err as &(dyn error::Error + 'static))
}
}
#[repr(C)]
pub struct ConstBody {
context: &'static str,
}
#[repr(C)]
struct DynBody<S, E = (), P = (), C = ()>
where
S: 'static,
E: 'static,
P: 'static,
C: 'static,
{
state: S,
vtable: &'static DynBodyVTable<S>,
source: E,
store: Store<P, C>,
}
#[repr(C)]
struct Store<P, L>
where
P: 'static,
L: 'static,
{
payload: P,
context: L,
}
struct DynBodyVTable<S>
where
S: 'static,
{
into_state: unsafe fn(Align4Own<DynBody<S>>) -> S,
into_source:
unsafe fn(Align4Own<DynBody<S>>) -> Option<Box<dyn error::Error + Send + Sync + 'static>>,
into_parts: unsafe fn(Align4Own<DynBody<S>>, TypeId, NonNull<()>, TypeId, NonNull<()>) -> S,
source: unsafe fn(Ref<'_, DynBody<S>>) -> Option<&(dyn error::Error + Send + Sync + 'static)>,
payload: unsafe fn(Ref<'_, DynBody<S>>) -> Option<&(dyn Display + Send + Sync + 'static)>,
context: unsafe fn(Ref<'_, DynBody<S>>) -> Option<&(dyn Display + Send + Sync + 'static)>,
downcast_source_ref: unsafe fn(Ref<'_, DynBody<S>>, TypeId) -> Option<Ref<'_, ()>>,
downcast_payload_ref: unsafe fn(Ref<'_, DynBody<S>>, TypeId) -> Option<Ref<'_, ()>>,
downcast_source_mut: unsafe fn(Mut<'_, DynBody<S>>, TypeId) -> Option<Mut<'_, ()>>,
downcast_payload_mut: unsafe fn(Mut<'_, DynBody<S>>, TypeId) -> Option<Mut<'_, ()>>,
}
impl<S> DynBodyVTable<S> {
const fn new<E, P, L>() -> Self
where
E: error::Error + Send + Sync + 'static,
L: Display + Send + Sync + 'static,
P: Display + Send + Sync + 'static,
{
DynBodyVTable {
into_state: DynBody::<S, E, P, L>::into_state,
into_source: DynBody::<S, E, P, L>::into_source,
into_parts: DynBody::<S, E, P, L>::into_parts,
source: DynBody::<S, E, P, L>::source,
payload: DynBody::<S, E, P, L>::payload,
context: DynBody::<S, E, P, L>::context,
downcast_source_ref: DynBody::<S, E, P, L>::downcast_source_ref,
downcast_payload_ref: DynBody::<S, E, P, L>::downcast_payload_ref,
downcast_source_mut: DynBody::<S, E, P, L>::downcast_source_mut,
downcast_payload_mut: DynBody::<S, E, P, L>::downcast_payload_mut,
}
}
}
impl<S, E, P, C> DynBody<S, E, P, C>
where
S: 'static,
E: error::Error + Send + Sync + 'static,
P: Display + Send + Sync + 'static,
C: Display + Send + Sync + 'static,
{
unsafe fn into_state(this: Align4Own<DynBody<S>>) -> S {
let this = *unsafe { this.cast::<Self>().into_boxed() };
this.0.state
}
unsafe fn into_source(
this: Align4Own<DynBody<S>>,
) -> Option<Box<dyn error::Error + Send + Sync + 'static>> {
let this = unsafe { this.cast::<Self>() };
let Align4(this) = *unsafe { this.into_boxed() };
if TypeId::of::<E>() == TypeId::of::<Nae>() {
None
} else {
Some(Box::new(this.source))
}
}
unsafe fn into_parts(
this: Align4Own<DynBody<S>>,
source_ty: TypeId,
source_dst: NonNull<()>,
payload_ty: TypeId,
payload_dst: NonNull<()>,
) -> S {
let Align4(this) = *unsafe { this.cast::<Self>().into_boxed() };
if TypeId::of::<E>() == source_ty {
let dst = unsafe { source_dst.cast::<Option<E>>().as_mut() };
dst.replace(this.source);
}
if TypeId::of::<P>() == payload_ty {
let dst = unsafe { payload_dst.cast::<Option<P>>().as_mut() };
dst.replace(this.store.payload);
}
this.state
}
unsafe fn source(
this: Ref<'_, DynBody<S>>,
) -> Option<&(dyn error::Error + Send + Sync + 'static)> {
let this = unsafe { this.cast::<Self>() };
let source = unsafe { this.project(|body| &raw const (*body).source) };
let err = source.deref();
if TypeId::of::<E>() == TypeId::of::<Nae>() {
None
} else {
Some(err as &(dyn error::Error + Send + Sync + 'static))
}
}
unsafe fn payload(this: Ref<'_, DynBody<S>>) -> Option<&(dyn Display + Send + Sync + 'static)> {
let this = unsafe { this.cast::<Self>() };
let payload = unsafe { this.project(|body| &raw const (*body).store.payload) };
if TypeId::of::<P>() == TypeId::of::<payload::Empty>() {
None
} else {
Some(payload.deref() as &(dyn Display + Send + Sync + 'static))
}
}
unsafe fn context(this: Ref<'_, DynBody<S>>) -> Option<&(dyn Display + Send + Sync + 'static)> {
let this = unsafe { this.cast::<Self>() };
let context = unsafe { this.project(|body| &raw const (*body).store.context) };
if TypeId::of::<C>() == TypeId::of::<context::Unit>() {
None
} else {
Some(context.deref() as &(dyn Display + Send + Sync + 'static))
}
}
unsafe fn downcast_source_ref(this: Ref<'_, DynBody<S>>, ty: TypeId) -> Option<Ref<'_, ()>> {
let this = unsafe { this.cast::<Self>() };
if ty == TypeId::of::<E>() {
Some(unsafe { this.project(|body| &raw const (*body).source).cast::<()>() })
} else {
None
}
}
unsafe fn downcast_payload_ref(this: Ref<'_, DynBody<S>>, _ty: TypeId) -> Option<Ref<'_, ()>> {
let this = unsafe { this.cast::<Self>() };
if _ty == TypeId::of::<P>() {
Some(unsafe {
this.project(|body| &raw const (*body).store.payload)
.cast::<()>()
})
} else {
None
}
}
unsafe fn downcast_source_mut(this: Mut<'_, DynBody<S>>, _ty: TypeId) -> Option<Mut<'_, ()>> {
let this = unsafe { this.cast::<Self>() };
if _ty == TypeId::of::<E>() {
Some(unsafe { this.project(|body| &raw mut (*body).source).cast::<()>() })
} else {
None
}
}
unsafe fn downcast_payload_mut(this: Mut<'_, DynBody<S>>, _ty: TypeId) -> Option<Mut<'_, ()>> {
let this = unsafe { this.cast::<Self>() };
if _ty == TypeId::of::<P>() {
Some(unsafe {
this.project(|body| &raw mut (*body).store.payload)
.cast::<()>()
})
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
context::{Blank, Literal},
nae::Nae,
payload,
};
use std::{
error,
fmt::{self, Display},
mem,
};
#[derive(Debug)]
struct TestError(&'static str);
impl Display for TestError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl error::Error for TestError {}
struct TestContext;
impl Literal for TestContext {
const LITERAL: &'static str = "test context";
}
#[derive(Debug, PartialEq)]
struct TestPayload(u32);
impl Display for TestPayload {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "payload({})", self.0)
}
}
#[test]
fn kind_discriminates_const() {
let err = RawError::<()>::new_const::<TestContext>();
assert_eq!(err.kind(), RawError::<()>::KIND_CONST);
}
#[test]
fn kind_discriminates_inline() {
let err = RawError::new_inline(42u32);
assert_eq!(err.kind(), RawError::<u32>::KIND_INLINE);
}
#[test]
fn kind_discriminates_boxed() {
let err = RawError::new_boxed::<_, _, Blank>((), TestError("oops"), payload::Empty);
assert_eq!(err.kind(), RawError::<()>::KIND_BOXED);
}
#[test]
fn const_variant_context() {
let err = RawError::<()>::new_const::<TestContext>();
let ctx = err.context();
assert!(ctx.is_some());
assert_eq!(ctx.unwrap().to_string(), "test context");
}
#[test]
fn const_variant_source_is_none() {
let err = RawError::<()>::new_const::<TestContext>();
assert!(err.source().is_none());
}
#[test]
fn const_variant_payload_is_none() {
let err = RawError::<()>::new_const::<TestContext>();
assert!(err.payload().is_none());
}
#[test]
fn const_variant_into_state() {
let err = RawError::<()>::new_const::<TestContext>();
let state = err.into_state();
assert_eq!(state, ());
}
#[test]
fn inline_variant_state() {
let err = RawError::new_inline(42u32);
assert_eq!(*err.state(), 42);
}
#[test]
fn inline_variant_into_state() {
let err = RawError::new_inline(42u32);
assert_eq!(err.into_state(), 42);
}
#[test]
fn inline_variant_context_is_none() {
let err = RawError::new_inline(42u32);
assert!(err.context().is_none());
}
#[test]
fn inline_variant_source_is_none() {
let err = RawError::new_inline("hello");
assert!(err.source().is_none());
}
#[test]
fn inline_variant_payload_is_none() {
let err = RawError::new_inline("hello");
assert!(err.payload().is_none());
}
#[test]
fn boxed_variant_source() {
let err = RawError::new_boxed::<_, _, Blank>((), TestError("oops"), payload::Empty);
let src = err.source();
assert!(src.is_some());
assert_eq!(src.unwrap().to_string(), "oops");
}
#[test]
fn boxed_variant_downcast_source() {
let err = RawError::new_boxed::<_, _, Blank>((), TestError("oops"), payload::Empty);
let downcasted = err.downcast_source_ref::<TestError>();
assert!(downcasted.is_some());
assert_eq!(downcasted.unwrap().0, "oops");
}
#[test]
fn boxed_variant_downcast_source_wrong_type() {
let err = RawError::new_boxed::<_, _, Blank>((), TestError("oops"), payload::Empty);
let downcasted = err.downcast_source_ref::<String>();
assert!(downcasted.is_none());
}
#[test]
fn boxed_variant_downcast_source_mut() {
let mut err = RawError::new_boxed::<_, _, Blank>((), TestError("oops"), payload::Empty);
{
let downcasted = err.downcast_source_mut::<TestError>();
assert!(downcasted.is_some());
downcasted.unwrap().0 = "fixed";
}
let downcasted = err.downcast_source_ref::<TestError>();
assert_eq!(downcasted.unwrap().0, "fixed");
}
#[test]
fn boxed_variant_payload() {
let err = RawError::new_boxed::<_, _, Blank>((), TestError("oops"), TestPayload(42));
let pl = err.payload();
assert!(pl.is_some());
assert_eq!(pl.unwrap().to_string(), "payload(42)");
}
#[test]
fn boxed_variant_downcast_payload() {
let err = RawError::new_boxed::<_, _, Blank>((), TestError("oops"), TestPayload(42));
let downcasted = err.downcast_payload_ref::<TestPayload>();
assert!(downcasted.is_some());
assert_eq!(downcasted.unwrap().0, 42);
}
#[test]
fn boxed_variant_context() {
let err = RawError::new_boxed::<_, _, TestContext>((), TestError("oops"), payload::Empty);
let ctx = err.context();
assert!(ctx.is_some());
assert_eq!(ctx.unwrap().to_string(), "test context");
}
#[test]
fn boxed_variant_nae_source_is_none() {
let err = RawError::new_boxed::<_, _, Blank>(42u32, Nae, payload::Empty);
assert!(err.source().is_none());
assert_eq!(*err.state(), 42);
}
#[test]
fn boxed_variant_empty_payload_is_none() {
let err = RawError::new_boxed::<_, _, Blank>((), TestError("oops"), payload::Empty);
assert!(err.payload().is_none());
}
#[test]
fn boxed_variant_into_source_returns_boxed_error() {
let err = RawError::new_boxed::<_, _, Blank>((), TestError("oops"), payload::Empty);
let src = err.into_source();
assert!(src.is_some());
assert_eq!(src.unwrap().to_string(), "oops");
}
#[test]
fn boxed_variant_into_source_nae_returns_none() {
let err = RawError::new_boxed::<_, _, Blank>((), Nae, payload::Empty);
assert!(err.into_source().is_none());
}
#[test]
fn boxed_variant_into_parts_matches_types() {
let err =
RawError::new_boxed::<_, _, TestContext>("state", TestError("oops"), TestPayload(99));
let (source, payload, state) = err.into_parts::<TestError, TestPayload>();
assert_eq!(state, "state");
assert!(source.is_some());
assert_eq!(source.unwrap().0, "oops");
assert!(payload.is_some());
assert_eq!(payload.unwrap().0, 99);
}
#[test]
fn boxed_variant_into_parts_wrong_source_type() {
let err = RawError::new_boxed::<_, _, Blank>((), TestError("oops"), payload::Empty);
let (source, payload, _) = err.into_parts::<String, payload::Empty>();
assert!(source.is_none());
assert!(payload.is_some());
}
#[test]
fn const_variant_into_parts() {
let err = RawError::<()>::new_const::<TestContext>();
let (source, payload, state) = err.into_parts::<TestError, TestPayload>();
assert!(source.is_none());
assert!(payload.is_none());
assert_eq!(state, ());
}
#[test]
fn inline_variant_into_parts() {
let err = RawError::new_inline(99u64);
let (source, payload, state) = err.into_parts::<TestError, TestPayload>();
assert!(source.is_none());
assert!(payload.is_none());
assert_eq!(state, 99);
}
#[test]
fn boxed_variant_drop_does_not_leak() {
use std::sync::atomic::{AtomicBool, Ordering};
static DROPPED: AtomicBool = AtomicBool::new(false);
#[derive(Debug)]
struct DropWatch;
impl Drop for DropWatch {
fn drop(&mut self) {
DROPPED.store(true, Ordering::SeqCst);
}
}
impl Display for DropWatch {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "")
}
}
impl error::Error for DropWatch {}
{
let _err = RawError::new_boxed::<_, _, Blank>((), DropWatch, payload::Empty);
} assert!(DROPPED.load(Ordering::SeqCst));
}
#[test]
fn const_variant_state_is_unit() {
let err = RawError::<()>::new_const::<TestContext>();
let s: &() = err.state();
assert_eq!(*s, ());
}
#[test]
fn raw_error_size() {
assert_eq!(mem::size_of::<RawError<()>>(), mem::size_of::<usize>());
}
}