1use thiserror::Error;
8
9#[derive(Debug, Error)]
13pub enum CodecError {
14 #[error("failed to encode {type_name}")]
15 Encode {
16 type_name: &'static str,
17 #[source]
18 source: Box<dyn core::error::Error + Send + Sync>,
19 },
20 #[error("failed to decode {type_name}")]
21 Decode {
22 type_name: &'static str,
23 #[source]
24 source: Box<dyn core::error::Error + Send + Sync>,
25 },
26 #[error("{type_name} did not survive an encode/decode round trip")]
27 RoundTripMismatch { type_name: &'static str },
28}
29
30#[cfg(test)]
31mod tests {
32 use super::*;
33 use core::error::Error as _;
34
35 #[derive(Debug, Error)]
36 #[error("the underlying detail")]
37 struct Underlying;
38
39 #[test]
40 fn every_variant_retains_its_cause() {
41 let e = CodecError::Decode { type_name: "PlMsg", source: Box::new(Underlying) };
42 assert_eq!(e.to_string(), "failed to decode PlMsg");
44 assert_eq!(e.source().expect("cause preserved").to_string(), "the underlying detail");
46 }
47
48 #[test]
49 fn encode_and_decode_are_distinguishable() {
50 let d = CodecError::Decode { type_name: "T", source: Box::new(Underlying) };
51 let e = CodecError::Encode { type_name: "T", source: Box::new(Underlying) };
52 assert_ne!(d.to_string(), e.to_string());
53 assert!(matches!(d, CodecError::Decode { .. }));
54 assert!(matches!(e, CodecError::Encode { .. }));
55 }
56
57 #[test]
58 fn round_trip_mismatch_names_the_type() {
59 let e = CodecError::RoundTripMismatch { type_name: "BebMsg" };
60 assert!(e.to_string().contains("BebMsg"));
61 assert!(e.source().is_none());
62 }
63}