1use std::fmt;
4
5pub type WireResult<T> = Result<T, WireError>;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum WireError {
11 PacketTooSmall {
13 actual: usize,
15 required: usize,
17 },
18
19 InvalidMagic {
21 found: u32,
23 },
24
25 UnsupportedVersion {
27 found: u16,
29 },
30
31 InvalidFlags {
33 flags: u16,
35 },
36
37 SchemaMismatch {
39 packet_hash: u64,
41 expected_hash: u64,
43 },
44
45 PacketTooLarge {
47 actual: usize,
49 limit: usize,
51 },
52
53 TooManySections {
55 count: usize,
57 limit: usize,
59 },
60
61 TooManyEntities {
63 operation: &'static str,
65 count: usize,
67 limit: usize,
69 },
70
71 UnknownSectionTag {
73 tag: u8,
75 },
76
77 PayloadLengthMismatch {
79 declared: usize,
81 actual: usize,
83 },
84
85 BitError(bitstream::BitError),
87}
88
89impl fmt::Display for WireError {
90 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91 match self {
92 Self::PacketTooSmall { actual, required } => {
93 write!(
94 f,
95 "packet too small: {actual} bytes, need at least {required}"
96 )
97 }
98 Self::InvalidMagic { found } => {
99 write!(f, "invalid magic number: 0x{found:08X}")
100 }
101 Self::UnsupportedVersion { found } => {
102 write!(f, "unsupported wire version: {found}")
103 }
104 Self::InvalidFlags { flags } => {
105 write!(f, "invalid flags: 0x{flags:04X}")
106 }
107 Self::SchemaMismatch {
108 packet_hash,
109 expected_hash,
110 } => {
111 write!(
112 f,
113 "schema mismatch: packet has 0x{packet_hash:016X}, expected 0x{expected_hash:016X}"
114 )
115 }
116 Self::PacketTooLarge { actual, limit } => {
117 write!(
118 f,
119 "packet too large: {actual} bytes exceeds limit of {limit}"
120 )
121 }
122 Self::TooManySections { count, limit } => {
123 write!(f, "too many sections: {count} exceeds limit of {limit}")
124 }
125 Self::TooManyEntities {
126 operation,
127 count,
128 limit,
129 } => {
130 write!(
131 f,
132 "too many entities in {operation}: {count} exceeds limit of {limit}"
133 )
134 }
135 Self::UnknownSectionTag { tag } => {
136 write!(f, "unknown section tag: {tag}")
137 }
138 Self::PayloadLengthMismatch { declared, actual } => {
139 write!(
140 f,
141 "payload length mismatch: declared {declared} bytes but {actual} available"
142 )
143 }
144 Self::BitError(e) => {
145 write!(f, "bitstream error: {e}")
146 }
147 }
148 }
149}
150
151impl std::error::Error for WireError {
152 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
153 match self {
154 Self::BitError(e) => Some(e),
155 _ => None,
156 }
157 }
158}
159
160impl From<bitstream::BitError> for WireError {
161 fn from(err: bitstream::BitError) -> Self {
162 Self::BitError(err)
163 }
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169
170 #[test]
171 fn error_display_packet_too_small() {
172 let err = WireError::PacketTooSmall {
173 actual: 10,
174 required: 28,
175 };
176 let msg = err.to_string();
177 assert!(msg.contains("10"), "should mention actual size");
178 assert!(msg.contains("28"), "should mention required size");
179 }
180
181 #[test]
182 fn error_display_invalid_magic() {
183 let err = WireError::InvalidMagic { found: 0xDEAD_BEEF };
184 let msg = err.to_string();
185 assert!(msg.contains("DEADBEEF"), "should show hex magic");
186 }
187
188 #[test]
189 fn error_display_unsupported_version() {
190 let err = WireError::UnsupportedVersion { found: 99 };
191 let msg = err.to_string();
192 assert!(msg.contains("99"), "should mention version");
193 }
194
195 #[test]
196 fn error_display_invalid_flags() {
197 let err = WireError::InvalidFlags { flags: 0xFF00 };
198 let msg = err.to_string();
199 assert!(msg.contains("FF00"), "should show hex flags");
200 }
201
202 #[test]
203 fn error_display_schema_mismatch() {
204 let err = WireError::SchemaMismatch {
205 packet_hash: 0x1234,
206 expected_hash: 0x5678,
207 };
208 let msg = err.to_string();
209 assert!(msg.contains("1234"), "should show packet hash");
210 assert!(msg.contains("5678"), "should show expected hash");
211 }
212
213 #[test]
214 fn error_display_packet_too_large() {
215 let err = WireError::PacketTooLarge {
216 actual: 100_000,
217 limit: 65536,
218 };
219 let msg = err.to_string();
220 assert!(msg.contains("100000"), "should mention actual size");
221 assert!(msg.contains("65536"), "should mention limit");
222 }
223
224 #[test]
225 fn error_display_too_many_sections() {
226 let err = WireError::TooManySections {
227 count: 100,
228 limit: 16,
229 };
230 let msg = err.to_string();
231 assert!(msg.contains("100"), "should mention count");
232 assert!(msg.contains("16"), "should mention limit");
233 }
234
235 #[test]
236 fn error_display_too_many_entities() {
237 let err = WireError::TooManyEntities {
238 operation: "create",
239 count: 500,
240 limit: 256,
241 };
242 let msg = err.to_string();
243 assert!(msg.contains("create"), "should mention operation");
244 assert!(msg.contains("500"), "should mention count");
245 assert!(msg.contains("256"), "should mention limit");
246 }
247
248 #[test]
249 fn error_display_unknown_section_tag() {
250 let err = WireError::UnknownSectionTag { tag: 42 };
251 let msg = err.to_string();
252 assert!(msg.contains("42"), "should mention tag");
253 }
254
255 #[test]
256 fn error_display_payload_length_mismatch() {
257 let err = WireError::PayloadLengthMismatch {
258 declared: 100,
259 actual: 50,
260 };
261 let msg = err.to_string();
262 assert!(msg.contains("100"), "should mention declared");
263 assert!(msg.contains("50"), "should mention actual");
264 }
265
266 #[test]
267 fn error_from_bit_error() {
268 let bit_err = bitstream::BitError::UnexpectedEof {
269 requested: 8,
270 available: 0,
271 };
272 let wire_err: WireError = bit_err.into();
273 assert!(matches!(wire_err, WireError::BitError(_)));
274
275 let msg = wire_err.to_string();
276 assert!(msg.contains("bitstream"), "should mention source");
277 }
278
279 #[test]
280 fn error_source_bit_error() {
281 let bit_err = bitstream::BitError::UnexpectedEof {
282 requested: 8,
283 available: 0,
284 };
285 let wire_err = WireError::BitError(bit_err);
286
287 let source = std::error::Error::source(&wire_err);
289 assert!(source.is_some(), "should have a source");
290 }
291
292 #[test]
293 fn error_source_none_for_others() {
294 let err = WireError::InvalidMagic { found: 0 };
295 let source = std::error::Error::source(&err);
296 assert!(source.is_none(), "non-wrapped errors should have no source");
297 }
298
299 #[test]
300 fn error_equality() {
301 let err1 = WireError::InvalidMagic { found: 0x1234 };
302 let err2 = WireError::InvalidMagic { found: 0x1234 };
303 let err3 = WireError::InvalidMagic { found: 0x5678 };
304 assert_eq!(err1, err2);
305 assert_ne!(err1, err3);
306 }
307
308 #[test]
309 fn error_is_std_error() {
310 fn assert_error<E: std::error::Error>() {}
311 assert_error::<WireError>();
312 }
313}