thc/
error.rs

1use std::{error::Error, fmt};
2
3/// Errors related to the decoding process.
4#[derive(Clone, Copy, Debug)]
5#[non_exhaustive]
6pub enum DecodingError {
7    /// Invalid header.
8    InvalidHeader(&'static str),
9    /// Missing tag bit at the given position.
10    MissingTag(usize),
11    /// Invalid cell index.
12    InvalidCellIndex {
13        /// Error message.
14        reason: &'static str,
15        /// Underlying validation error, if any.
16        source: Option<h3o::error::InvalidCellIndex>,
17    },
18    /// Not enough data to decompress.
19    NotEnoughData,
20}
21
22impl DecodingError {
23    pub(crate) const fn bad_header(reason: &'static str) -> Self {
24        Self::InvalidHeader(reason)
25    }
26
27    pub(crate) const fn missing_tag(position: usize) -> Self {
28        Self::MissingTag(position)
29    }
30
31    pub(crate) const fn bad_index(
32        reason: &'static str,
33        source: Option<h3o::error::InvalidCellIndex>,
34    ) -> Self {
35        Self::InvalidCellIndex { reason, source }
36    }
37
38    pub(crate) const fn not_enough_data() -> Self {
39        Self::NotEnoughData
40    }
41}
42
43impl fmt::Display for DecodingError {
44    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45        match *self {
46            Self::InvalidHeader(reason) => {
47                write!(f, "header error: {reason}")
48            }
49            Self::MissingTag(position) => {
50                write!(f, "missing tag bit at {position}")
51            }
52            Self::InvalidCellIndex { reason, .. } => {
53                write!(f, "invalid cell index: {reason}")
54            }
55            Self::NotEnoughData => write!(f, "truncated input"),
56        }
57    }
58}
59
60impl Error for DecodingError {
61    fn source(&self) -> Option<&(dyn Error + 'static)> {
62        if let Self::InvalidCellIndex {
63            source: Some(ref source),
64            ..
65        } = *self
66        {
67            return Some(source);
68        }
69        None
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76    use h3o::CellIndex;
77
78    // All error must have a non-empty display.
79    #[test]
80    fn display() {
81        assert!(!DecodingError::bad_header("invalid header")
82            .to_string()
83            .is_empty());
84
85        assert!(!DecodingError::missing_tag(42).to_string().is_empty());
86
87        assert!(!DecodingError::bad_index("invalid cell index", None)
88            .to_string()
89            .is_empty());
90
91        assert!(!DecodingError::not_enough_data().to_string().is_empty());
92    }
93
94    // Check that source if forwarded when relevant.
95    #[test]
96    fn source() {
97        assert!(DecodingError::bad_header("invalid header")
98            .source()
99            .is_none());
100
101        assert!(DecodingError::missing_tag(42).source().is_none());
102
103        assert!(DecodingError::bad_index("invalid cell index", None)
104            .source()
105            .is_none());
106        assert!(DecodingError::bad_index(
107            "not a cell index",
108            CellIndex::try_from(0).err()
109        )
110        .source()
111        .is_some());
112
113        assert!(DecodingError::not_enough_data().source().is_none());
114    }
115}