use core::error::Error;
use core::fmt::{Debug, Display, Formatter};
use core::hash::{Hash, Hasher};
pub struct DisplayError<E> {
inner: E,
get_source: fn(&E) -> Option<&(dyn core::error::Error + 'static)>,
}
impl<E: Debug + Display> DisplayError<E> {
#[must_use]
pub fn new(error: E) -> Self {
Self {
inner: error,
get_source: |_| None,
}
}
pub(crate) fn with_get_source(
error: E,
get_source: fn(&E) -> Option<&(dyn core::error::Error + 'static)>,
) -> Self {
Self {
inner: error,
get_source,
}
}
}
impl<E> DisplayError<E> {
#[must_use]
pub fn inner(&self) -> &E {
&self.inner
}
#[must_use]
pub fn into_inner(self) -> E {
self.inner
}
}
impl<E: Clone> Clone for DisplayError<E> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
get_source: self.get_source,
}
}
}
impl<E: Display> Display for DisplayError<E> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
Display::fmt(&self.inner, f)
}
}
impl<E: Debug> Debug for DisplayError<E> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
Debug::fmt(&self.inner, f)
}
}
impl<E: PartialEq> PartialEq for DisplayError<E> {
fn eq(&self, other: &Self) -> bool {
self.inner == other.inner
}
}
impl<E: Eq> Eq for DisplayError<E> {}
impl<E: Hash> Hash for DisplayError<E> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.inner.hash(state);
}
}
impl<E: Debug + Display> Error for DisplayError<E> {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
(self.get_source)(&self.inner)
}
}
#[cfg(test)]
mod tests {
use super::*;
struct FakeLibError {
message: &'static str,
}
impl Display for FakeLibError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.message)
}
}
impl Debug for FakeLibError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "FakeLibError({})", self.message)
}
}
#[test]
fn test_new_and_into_inner() {
let original = FakeLibError { message: "oops" };
let wrapped = DisplayError::new(original);
let inner = wrapped.into_inner();
assert_eq!(inner.message, "oops");
}
#[test]
fn test_inner_ref() {
let wrapped = DisplayError::new(FakeLibError {
message: "ref access",
});
assert_eq!(wrapped.inner().message, "ref access");
}
#[test]
fn test_clone() {
#[derive(Clone)]
struct ClonableError {
message: &'static str,
}
impl Display for ClonableError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.message)
}
}
impl Debug for ClonableError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "ClonableError({})", self.message)
}
}
let original = DisplayError::new(ClonableError { message: "test" });
let cloned = original.clone();
assert_eq!(cloned.inner().message, "test");
}
#[test]
fn test_error_source_is_none() {
let wrapped = DisplayError::new(FakeLibError {
message: "no source",
});
let err: &dyn Error = &wrapped;
assert!(err.source().is_none());
}
#[test]
fn test_with_get_source_none_for_non_error() {
let wrapped = DisplayError::with_get_source(
FakeLibError {
message: "no error impl",
},
|_| None,
);
let err: &dyn Error = &wrapped;
assert!(err.source().is_none());
}
#[test]
fn test_partial_eq() {
let a = DisplayError::new(42);
let b = DisplayError::new(42);
let c = DisplayError::new(99);
assert_eq!(a, b);
assert_ne!(a, c);
}
#[test]
fn test_hash() {
struct SimpleHasher(u64);
impl Hasher for SimpleHasher {
fn finish(&self) -> u64 {
self.0
}
fn write(&mut self, bytes: &[u8]) {
for &b in bytes {
self.0 = self.0.wrapping_mul(31).wrapping_add(b as u64);
}
}
}
fn hash_one<T: Hash>(val: &T) -> u64 {
let mut h = SimpleHasher(0);
val.hash(&mut h);
h.finish()
}
let a = DisplayError::new(42);
let b = DisplayError::new(42);
assert_eq!(hash_one(&a), hash_one(&b));
}
#[cfg(feature = "alloc")]
mod alloc_tests {
use super::*;
#[test]
fn test_with_get_source_delegates_to_inner() {
#[derive(Debug)]
struct InnerError;
impl Display for InnerError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.write_str("inner")
}
}
impl Error for InnerError {}
#[derive(Debug)]
struct OuterError(InnerError);
impl Display for OuterError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.write_str("outer")
}
}
impl Error for OuterError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
Some(&self.0)
}
}
let wrapped = DisplayError::with_get_source(OuterError(InnerError), |e| e.source());
let err: &dyn Error = &wrapped;
let source = err.source().expect("source should delegate");
assert_eq!(alloc::format!("{source}"), "inner");
}
#[test]
fn test_clone_preserves_source_delegation() {
#[derive(Clone, Debug)]
struct InnerError;
impl Display for InnerError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.write_str("inner")
}
}
impl Error for InnerError {}
#[derive(Clone, Debug)]
struct OuterError(InnerError);
impl Display for OuterError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.write_str("outer")
}
}
impl Error for OuterError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
Some(&self.0)
}
}
let original = DisplayError::with_get_source(OuterError(InnerError), |e| e.source());
let cloned = original.clone();
let err: &dyn Error = &cloned;
let source = err
.source()
.expect("clone should preserve source delegation");
assert_eq!(alloc::format!("{source}"), "inner");
}
#[test]
fn test_display_delegates() {
let wrapped = DisplayError::new(FakeLibError {
message: "display me",
});
let s = alloc::format!("{wrapped}");
assert_eq!(s, "display me");
}
#[test]
fn test_debug_delegates() {
let wrapped = DisplayError::new(FakeLibError {
message: "debug me",
});
let s = alloc::format!("{wrapped:?}");
assert_eq!(s, "FakeLibError(debug me)");
}
}
}