Skip to main content

boon/
error.rs

1use std::fmt;
2
3/// Errors that can occur while parsing a demo file.
4#[derive(thiserror::Error, Debug)]
5pub enum Error {
6    #[error("IO error: {0}")]
7    Io(#[from] std::io::Error),
8    #[error("invalid demo file: magic bytes mismatch (expected PBDEMS2\\0, got {got:?})")]
9    InvalidMagic { got: [u8; 8] },
10    #[error("unexpected end of data: needed {needed} bits, have {available}")]
11    Overflow { needed: usize, available: usize },
12    #[error("protobuf decode error: {0}")]
13    Decode(#[from] prost::DecodeError),
14    #[error("decompression error: {0}")]
15    Decompress(String),
16    #[error("unknown command type: {0}")]
17    UnknownCommand(u32),
18    #[error("parse error: {context}")]
19    Parse { context: String },
20}
21
22// Manual impl because `snap::Error` doesn't implement `std::error::Error`,
23// so thiserror's `#[from]` derive can't be used.
24impl From<snap::Error> for Error {
25    fn from(e: snap::Error) -> Self {
26        Error::Decompress(e.to_string())
27    }
28}
29
30/// Convenience alias used throughout the crate.
31pub type Result<T> = std::result::Result<T, Error>;
32
33/// Lightweight error type for field value conversions.
34#[derive(Debug)]
35pub struct FieldValueConversionError;
36
37impl fmt::Display for FieldValueConversionError {
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        write!(
40            f,
41            "incompatible types or out of range integer conversion attempted"
42        )
43    }
44}
45
46impl std::error::Error for FieldValueConversionError {}
47
48#[cfg(test)]
49mod tests {
50    use super::*;
51
52    #[test]
53    fn io_error_display() {
54        let err = Error::Io(std::io::Error::new(std::io::ErrorKind::NotFound, "gone"));
55        let msg = format!("{}", err);
56        assert!(msg.contains("IO error"));
57    }
58
59    #[test]
60    fn invalid_magic_display() {
61        let err = Error::InvalidMagic { got: [0; 8] };
62        let msg = format!("{}", err);
63        assert!(msg.contains("magic bytes"));
64    }
65
66    #[test]
67    fn overflow_display() {
68        let err = Error::Overflow {
69            needed: 64,
70            available: 8,
71        };
72        let msg = format!("{}", err);
73        assert!(msg.contains("64"));
74        assert!(msg.contains("8"));
75    }
76
77    #[test]
78    fn parse_error_display() {
79        let err = Error::Parse {
80            context: "bad data".to_string(),
81        };
82        let msg = format!("{}", err);
83        assert!(msg.contains("bad data"));
84    }
85
86    #[test]
87    fn field_value_conversion_error() {
88        let err = FieldValueConversionError;
89        let msg = format!("{}", err);
90        assert!(msg.contains("incompatible"));
91        // Verify it implements std::error::Error
92        let _: &dyn std::error::Error = &err;
93    }
94}