Skip to main content

spvirit_codec/
error.rs

1//! Typed decode errors for the PVA and PVD codecs.
2
3use std::fmt;
4
5/// Everything that can go wrong decoding a PVA frame or a PVD value.
6///
7/// Replaces the bare `None` the decoders used to return, which could not
8/// distinguish "buffer ran out" from "this array is implausibly large".
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum DecodeError {
11    /// The buffer ended before the field did.
12    Truncated { needed: usize, available: usize },
13    /// An array's element count exceeds the configured `DecodeLimits` entry.
14    ArrayTooLarge { kind: &'static str, count: usize, limit: usize },
15    /// An array's element count cannot fit in the bytes that remain, so the
16    /// count itself is corrupt. Caught before allocating.
17    CountExceedsBuffer { count: usize, min_bytes: usize, available: usize },
18    /// Reassembly exceeded `SegmentReassembler`'s byte cap.
19    MessageTooLarge { total: usize, limit: usize },
20    /// A middle or last segment arrived with no message in progress, or a
21    /// first segment arrived while one was already in progress.
22    UnexpectedSegment { flags: u8 },
23    /// An unsegmented application message arrived mid-reassembly.
24    SegmentInterrupted { expected: u8, got: u8 },
25    /// Segments of one message disagree on command byte or direction.
26    SegmentCommandMismatch { expected: u8, got: u8 },
27    /// An introspection tag byte we do not recognise.
28    UnknownTypeTag(u8),
29    /// A `0xFE` "only id" reference with no matching cached type. Usually
30    /// means the `PvdDecoder` was not reused across the connection.
31    UnresolvedTypeId(u16),
32    /// A union selector outside the union's field list.
33    UnknownUnionSelector { selector: usize, len: usize },
34    /// Structurally invalid input that needs no parameters to explain.
35    Malformed(&'static str),
36}
37
38impl fmt::Display for DecodeError {
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        match self {
41            Self::Truncated { needed, available } => {
42                write!(f, "truncated: need {needed} bytes, {available} available")
43            }
44            Self::ArrayTooLarge { kind, count, limit } => {
45                write!(f, "{kind} of {count} elements exceeds the limit of {limit}")
46            }
47            Self::CountExceedsBuffer { count, min_bytes, available } => write!(
48                f,
49                "element count {count} needs at least {min_bytes} bytes, {available} available"
50            ),
51            Self::MessageTooLarge { total, limit } => {
52                write!(f, "reassembled message of {total} bytes exceeds the limit of {limit}")
53            }
54            Self::UnexpectedSegment { flags } => {
55                write!(f, "unexpected segment, flags 0x{flags:02x}")
56            }
57            Self::SegmentInterrupted { expected, got } => write!(
58                f,
59                "reassembly of command {expected} interrupted by unsegmented command {got}"
60            ),
61            Self::SegmentCommandMismatch { expected, got } => {
62                write!(f, "segment command mismatch: expected {expected}, got {got}")
63            }
64            Self::UnknownTypeTag(tag) => write!(f, "unknown type tag 0x{tag:02x}"),
65            Self::UnresolvedTypeId(id) => {
66                write!(f, "unresolved introspection id {id}: decoder not reused across connection?")
67            }
68            Self::UnknownUnionSelector { selector, len } => {
69                write!(f, "union selector {selector} out of range for {len} fields")
70            }
71            Self::Malformed(what) => write!(f, "malformed: {what}"),
72        }
73    }
74}
75
76impl std::error::Error for DecodeError {}
77
78/// Result alias for the decode paths.
79pub type DecodeResult<T> = Result<T, DecodeError>;
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84
85    #[test]
86    fn display_names_the_offending_values() {
87        let e = DecodeError::ArrayTooLarge { kind: "string array", count: 900_000, limit: 65_536 };
88        assert_eq!(
89            e.to_string(),
90            "string array of 900000 elements exceeds the limit of 65536"
91        );
92    }
93
94    #[test]
95    fn errors_compare_by_value() {
96        let a = DecodeError::Truncated { needed: 12, available: 4 };
97        let b = DecodeError::Truncated { needed: 12, available: 4 };
98        let c = DecodeError::Truncated { needed: 12, available: 5 };
99        assert_eq!(a, b);
100        assert_ne!(a, c);
101    }
102
103    #[test]
104    fn implements_std_error() {
105        fn assert_error<E: std::error::Error>(_: &E) {}
106        assert_error(&DecodeError::Malformed("bad tag"));
107    }
108}