1use std::fmt;
4
5pub type CodecResult<T> = Result<T, CodecError>;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum CodecError {
11 Wire(wire::WireError),
13
14 BaselineNotFound {
16 requested_tick: u32,
18 },
19
20 EntityNotFound {
22 entity_id: u32,
24 },
25
26 ComponentNotFound {
28 entity_id: u32,
30 component_id: u16,
32 },
33
34 DuplicateEntity {
36 entity_id: u32,
38 },
39
40 EntityAlreadyExists {
42 entity_id: u32,
44 },
45}
46
47impl fmt::Display for CodecError {
48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49 match self {
50 Self::Wire(e) => write!(f, "wire error: {e}"),
51 Self::BaselineNotFound { requested_tick } => {
52 write!(f, "baseline tick {requested_tick} not found in history")
53 }
54 Self::EntityNotFound { entity_id } => {
55 write!(f, "entity {entity_id} not found")
56 }
57 Self::ComponentNotFound {
58 entity_id,
59 component_id,
60 } => {
61 write!(
62 f,
63 "component {component_id} not found on entity {entity_id}"
64 )
65 }
66 Self::DuplicateEntity { entity_id } => {
67 write!(f, "duplicate entity {entity_id} in create section")
68 }
69 Self::EntityAlreadyExists { entity_id } => {
70 write!(f, "entity {entity_id} already exists")
71 }
72 }
73 }
74}
75
76impl std::error::Error for CodecError {
77 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
78 match self {
79 Self::Wire(e) => Some(e),
80 _ => None,
81 }
82 }
83}
84
85impl From<wire::WireError> for CodecError {
86 fn from(err: wire::WireError) -> Self {
87 Self::Wire(err)
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 #[test]
96 fn error_display_baseline_not_found() {
97 let err = CodecError::BaselineNotFound { requested_tick: 42 };
98 let msg = err.to_string();
99 assert!(msg.contains("42"), "should mention tick");
100 assert!(msg.contains("baseline"), "should mention baseline");
101 }
102
103 #[test]
104 fn error_display_entity_not_found() {
105 let err = CodecError::EntityNotFound { entity_id: 123 };
106 let msg = err.to_string();
107 assert!(msg.contains("123"), "should mention entity id");
108 }
109
110 #[test]
111 fn error_display_component_not_found() {
112 let err = CodecError::ComponentNotFound {
113 entity_id: 10,
114 component_id: 5,
115 };
116 let msg = err.to_string();
117 assert!(msg.contains("10"), "should mention entity id");
118 assert!(msg.contains('5'), "should mention component id");
119 }
120
121 #[test]
122 fn error_display_duplicate_entity() {
123 let err = CodecError::DuplicateEntity { entity_id: 42 };
124 let msg = err.to_string();
125 assert!(msg.contains("42"), "should mention entity id");
126 assert!(msg.contains("duplicate"), "should mention duplicate");
127 }
128
129 #[test]
130 fn error_display_entity_already_exists() {
131 let err = CodecError::EntityAlreadyExists { entity_id: 99 };
132 let msg = err.to_string();
133 assert!(msg.contains("99"), "should mention entity id");
134 assert!(msg.contains("exists"), "should mention exists");
135 }
136
137 #[test]
138 fn error_from_wire_error() {
139 let wire_err = wire::WireError::InvalidMagic { found: 0x1234 };
140 let codec_err: CodecError = wire_err.into();
141 assert!(matches!(codec_err, CodecError::Wire(_)));
142 }
143
144 #[test]
145 fn error_source_wire() {
146 let wire_err = wire::WireError::InvalidMagic { found: 0x1234 };
147 let codec_err = CodecError::Wire(wire_err);
148 let source = std::error::Error::source(&codec_err);
149 assert!(source.is_some(), "should have a source");
150 }
151
152 #[test]
153 fn error_source_none_for_others() {
154 let err = CodecError::EntityNotFound { entity_id: 1 };
155 let source = std::error::Error::source(&err);
156 assert!(source.is_none(), "non-wrapped errors should have no source");
157 }
158
159 #[test]
160 fn error_equality() {
161 let err1 = CodecError::EntityNotFound { entity_id: 42 };
162 let err2 = CodecError::EntityNotFound { entity_id: 42 };
163 let err3 = CodecError::EntityNotFound { entity_id: 43 };
164
165 assert_eq!(err1, err2);
166 assert_ne!(err1, err3);
167 }
168
169 #[test]
170 fn error_is_std_error() {
171 fn assert_error<E: std::error::Error>() {}
172 assert_error::<CodecError>();
173 }
174}