Skip to main content

crabka_protocol/records/
error.rs

1//! Errors specific to record-batch decoding/encoding.
2
3use thiserror::Error;
4
5#[derive(Debug, Error)]
6#[non_exhaustive]
7pub enum RecordsError {
8    #[error("buffer too short for batch header (need {needed} more bytes)")]
9    HeaderTooShort { needed: usize },
10
11    #[error("batch magic byte {found} unsupported (only v2 supported)")]
12    UnsupportedMagic { found: i8 },
13
14    #[error("CRC mismatch: expected {expected:#010x}, computed {computed:#010x}")]
15    CrcMismatch { expected: u32, computed: u32 },
16
17    #[error("batch body truncated (need {needed} more bytes)")]
18    BodyTooShort { needed: usize },
19
20    #[error("record parse failed: {0}")]
21    RecordParse(String),
22
23    #[error("compression: {0}")]
24    Compression(#[from] crabka_compression::CompressionError),
25
26    #[error("zerocopy reinterpretation failed")]
27    ZerocopyFailure,
28}
29
30impl From<RecordsError> for crate::ProtocolError {
31    fn from(e: RecordsError) -> Self {
32        crate::ProtocolError::InvalidValue(match e {
33            RecordsError::HeaderTooShort { .. } => "records: header too short",
34            RecordsError::UnsupportedMagic { .. } => "records: unsupported magic",
35            RecordsError::CrcMismatch { .. } => "records: CRC mismatch",
36            RecordsError::BodyTooShort { .. } => "records: body truncated",
37            RecordsError::RecordParse(_) => "records: record parse failed",
38            RecordsError::Compression(_) => "records: compression error",
39            RecordsError::ZerocopyFailure => "records: zerocopy failure",
40        })
41    }
42}
43
44#[cfg(test)]
45mod tests {
46    use assert2::assert;
47
48    use super::*;
49
50    #[test]
51    fn display_messages() {
52        let cases: &[(RecordsError, &str)] = &[
53            (
54                RecordsError::HeaderTooShort { needed: 4 },
55                "buffer too short for batch header",
56            ),
57            (
58                RecordsError::UnsupportedMagic { found: 1 },
59                "batch magic byte 1 unsupported",
60            ),
61            (
62                RecordsError::CrcMismatch {
63                    expected: 0xDEAD_BEEF,
64                    computed: 0x1234_5678,
65                },
66                "CRC mismatch: expected 0xdeadbeef",
67            ),
68            (
69                RecordsError::BodyTooShort { needed: 17 },
70                "batch body truncated",
71            ),
72            (
73                RecordsError::RecordParse("bad varint".into()),
74                "record parse failed",
75            ),
76            (
77                RecordsError::ZerocopyFailure,
78                "zerocopy reinterpretation failed",
79            ),
80        ];
81        for (err, contains) in cases {
82            assert!(
83                err.to_string().contains(contains),
84                "{err} did not contain {contains:?}",
85            );
86        }
87    }
88
89    #[test]
90    fn into_protocol_error_is_invalid_value() {
91        let e: crate::ProtocolError = RecordsError::UnsupportedMagic { found: 0 }.into();
92        assert!(matches!(e, crate::ProtocolError::InvalidValue(_)));
93    }
94}