Skip to main content

h3o_zip/
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!(
82            !DecodingError::bad_header("invalid header")
83                .to_string()
84                .is_empty()
85        );
86
87        assert!(!DecodingError::missing_tag(42).to_string().is_empty());
88
89        assert!(
90            !DecodingError::bad_index("invalid cell index", None)
91                .to_string()
92                .is_empty()
93        );
94
95        assert!(!DecodingError::not_enough_data().to_string().is_empty());
96    }
97
98    // Check that source if forwarded when relevant.
99    #[test]
100    fn source() {
101        assert!(
102            DecodingError::bad_header("invalid header")
103                .source()
104                .is_none()
105        );
106
107        assert!(DecodingError::missing_tag(42).source().is_none());
108
109        assert!(
110            DecodingError::bad_index("invalid cell index", None)
111                .source()
112                .is_none()
113        );
114        assert!(
115            DecodingError::bad_index(
116                "not a cell index",
117                CellIndex::try_from(0).err()
118            )
119            .source()
120            .is_some()
121        );
122
123        assert!(DecodingError::not_enough_data().source().is_none());
124    }
125}