Skip to main content

wire/
error.rs

1//! Error types for wire format operations.
2
3use std::fmt;
4
5/// Result type for wire format operations.
6pub type WireResult<T> = Result<T, DecodeError>;
7
8/// High-level decode errors for wire framing.
9#[derive(Debug, Clone, PartialEq, Eq)]
10#[non_exhaustive]
11pub enum DecodeError {
12    /// Packet is too small to contain the required header.
13    PacketTooSmall { actual: usize, required: usize },
14
15    /// Invalid magic number in packet header.
16    InvalidMagic { found: u32 },
17
18    /// Unsupported wire version.
19    UnsupportedVersion { found: u16 },
20
21    /// Invalid flags combination.
22    InvalidFlags { flags: u16 },
23
24    /// Invalid baseline tick for the packet kind.
25    InvalidBaselineTick { baseline_tick: u32, flags: u16 },
26
27    /// Payload length mismatch.
28    PayloadLengthMismatch { header_len: u32, actual_len: usize },
29
30    /// Unknown section tag encountered.
31    UnknownSectionTag { tag: u8 },
32
33    /// Limits exceeded.
34    LimitsExceeded {
35        kind: LimitKind,
36        limit: usize,
37        actual: usize,
38    },
39
40    /// Section framing error.
41    SectionFraming(SectionFramingError),
42}
43
44/// Specific wire limits that can be exceeded.
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46pub enum LimitKind {
47    PacketBytes,
48    SectionCount,
49    SectionLength,
50}
51
52/// Errors that can occur while framing sections.
53#[derive(Debug, Clone, PartialEq, Eq)]
54pub enum SectionFramingError {
55    InvalidVarint,
56    LengthOverflow { value: u64 },
57    Truncated { needed: usize, available: usize },
58}
59
60/// Errors that can occur during encoding.
61#[derive(Debug, Clone, PartialEq, Eq)]
62pub enum EncodeError {
63    BufferTooSmall { needed: usize, available: usize },
64    LengthOverflow { length: usize },
65}
66
67impl fmt::Display for DecodeError {
68    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69        match self {
70            Self::PacketTooSmall { actual, required } => {
71                write!(
72                    f,
73                    "packet too small: {actual} bytes, need at least {required}"
74                )
75            }
76            Self::InvalidMagic { found } => {
77                write!(f, "invalid magic number: 0x{found:08X}")
78            }
79            Self::UnsupportedVersion { found } => {
80                write!(f, "unsupported wire version: {found}")
81            }
82            Self::InvalidFlags { flags } => {
83                write!(f, "invalid flags: 0x{flags:04X}")
84            }
85            Self::InvalidBaselineTick {
86                baseline_tick,
87                flags,
88            } => {
89                write!(
90                    f,
91                    "invalid baseline tick {baseline_tick} for flags 0x{flags:04X}"
92                )
93            }
94            Self::PayloadLengthMismatch {
95                header_len,
96                actual_len,
97            } => {
98                write!(
99                    f,
100                    "payload length mismatch: header {header_len} bytes but {actual_len} available"
101                )
102            }
103            Self::UnknownSectionTag { tag } => {
104                write!(f, "unknown section tag: {tag}")
105            }
106            Self::LimitsExceeded {
107                kind,
108                limit,
109                actual,
110            } => {
111                write!(f, "{kind} limit exceeded: {actual} > {limit}")
112            }
113            Self::SectionFraming(err) => write!(f, "section framing error: {err}"),
114        }
115    }
116}
117
118impl fmt::Display for LimitKind {
119    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120        let name = match self {
121            Self::PacketBytes => "packet bytes",
122            Self::SectionCount => "section count",
123            Self::SectionLength => "section length",
124        };
125        write!(f, "{name}")
126    }
127}
128
129impl fmt::Display for SectionFramingError {
130    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131        match self {
132            Self::InvalidVarint => write!(f, "invalid varint"),
133            Self::LengthOverflow { value } => write!(f, "length overflow: {value}"),
134            Self::Truncated { needed, available } => {
135                write!(
136                    f,
137                    "truncated section: need {needed} bytes, have {available}"
138                )
139            }
140        }
141    }
142}
143
144impl fmt::Display for EncodeError {
145    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
146        match self {
147            Self::BufferTooSmall { needed, available } => {
148                write!(f, "buffer too small: need {needed}, have {available}")
149            }
150            Self::LengthOverflow { length } => {
151                write!(f, "length overflow: {length}")
152            }
153        }
154    }
155}
156
157impl std::error::Error for DecodeError {}
158
159impl std::error::Error for EncodeError {}
160
161#[cfg(test)]
162mod tests {
163    use super::*;
164
165    #[test]
166    fn decode_error_display_invalid_magic() {
167        let err = DecodeError::InvalidMagic { found: 0xDEAD_BEEF };
168        let msg = err.to_string();
169        assert!(msg.contains("DEADBEEF"));
170    }
171
172    #[test]
173    fn decode_error_display_limits_exceeded() {
174        let err = DecodeError::LimitsExceeded {
175            kind: LimitKind::SectionCount,
176            limit: 4,
177            actual: 10,
178        };
179        let msg = err.to_string();
180        assert!(msg.contains("section count"));
181        assert!(msg.contains("10"));
182    }
183
184    #[test]
185    fn section_framing_display() {
186        let err = SectionFramingError::Truncated {
187            needed: 10,
188            available: 4,
189        };
190        let msg = err.to_string();
191        assert!(msg.contains("truncated"));
192        assert!(msg.contains("10"));
193    }
194
195    #[test]
196    fn encode_error_display() {
197        let err = EncodeError::BufferTooSmall {
198            needed: 10,
199            available: 4,
200        };
201        let msg = err.to_string();
202        assert!(msg.contains("buffer too small"));
203    }
204}