diff --git a/valuable-serde/src/lib.rs b/valuable-serde/src/lib.rs index 03d79422..47ab0003 100644 --- a/valuable-serde/src/lib.rs +++ b/valuable-serde/src/lib.rs @@ -264,7 +264,7 @@ where #[cfg(feature = "std")] Value::Path(p) => Serialize::serialize(p, serializer), #[cfg(feature = "std")] - Value::Error(e) => serializer.collect_str(e), + Value::Error(e) => SerializeError(e).serialize(serializer), v => unimplemented!("{:?}", v), } @@ -668,3 +668,19 @@ impl Visit for VisitDynamic<'_, S> { } } } + +struct SerializeError<'a>(&'a dyn std::error::Error); +impl Serialize for SerializeError<'_> { + fn serialize(&self, serializer: S) -> Result { + struct CollectStr<'a>(&'a dyn std::error::Error); + impl Serialize for CollectStr<'_> { + fn serialize(&self, serializer: S) -> Result { + serializer.collect_str(&self.0) + } + } + let mut s = serializer.serialize_struct("Error", 2)?; + s.serialize_field("message", &CollectStr(self.0))?; + s.serialize_field("source", &self.0.source().map(SerializeError))?; + s.end() + } +} diff --git a/valuable-serde/tests/test.rs b/valuable-serde/tests/test.rs index 3c9433b1..4e731039 100644 --- a/valuable-serde/tests/test.rs +++ b/valuable-serde/tests/test.rs @@ -518,3 +518,75 @@ fn test_dyn_enum() { ], ); } + +#[test] +fn test_errors() { + use std::{error::Error, fmt}; + + #[derive(Debug)] + struct TestError { + message: &'static str, + source: Option>, + } + + impl fmt::Display for TestError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.message) + } + } + + impl Error for TestError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + self.source.as_deref() + } + } + + let no_source = TestError { + message: "an error occurred", + source: None, + }; + + assert_ser_eq!( + &Serializable::new(&no_source as &(dyn Error + 'static)), + &[ + Token::Struct { + name: "Error", + len: 2 + }, + Token::Str("message"), + Token::Str("an error occurred"), + Token::Str("source"), + Token::None, + Token::StructEnd + ] + ); + + let with_source = TestError { + message: "the error caused another error", + source: Some(Box::new(no_source)), + }; + + assert_ser_eq!( + &Serializable::new(&with_source as &(dyn Error + 'static)), + &[ + Token::Struct { + name: "Error", + len: 2 + }, + Token::Str("message"), + Token::Str("the error caused another error"), + Token::Str("source"), + Token::Some, + Token::Struct { + name: "Error", + len: 2 + }, + Token::Str("message"), + Token::Str("an error occurred"), + Token::Str("source"), + Token::None, + Token::StructEnd, + Token::StructEnd + ] + ); +}