Skip to main content

cesr/b64/
error.rs

1#[cfg(feature = "alloc")]
2#[allow(
3    unused_imports,
4    reason = "alloc prelude items; subset used per cfg/feature combination"
5)]
6use alloc::{format, string::ToString, vec, vec::Vec};
7use thiserror::Error as ThisError;
8
9/// Errors from CESR Base64 encode/decode operations.
10#[derive(Debug, ThisError, PartialEq, Eq)]
11pub enum Error {
12    /// The decoded Base64 value exceeds the target integer type's maximum.
13    #[error(
14        "Integer Overflow: The decoded Base64 value exceeds the maximum size for the target integer type."
15    )]
16    IntegerOverflow,
17    /// A character was encountered that is not in the URL-safe Base64 alphabet.
18    #[error(
19        "Invalid Base64 Character: Encountered '{0}', which is not part of the URL-safe Base64 character set."
20    )]
21    InvalidBase64Char(char),
22    /// A numeric Base64 value (0–63) was out of bounds.
23    #[error(
24        "Invalid Base64 Value: The value {0} is out of bounds for the Base64 character set (0-63)."
25    )]
26    InvalidBase64Value(u8),
27
28    /// The input stream ended before enough bytes were available.
29    #[error(
30        "Short Binary Stream: More bytes were expected to complete the parsing operation, but the stream ended."
31    )]
32    ShortBinaryStream,
33
34    /// The input length is not a whole multiple of the conversion unit
35    /// (4 Base64 characters per 3 binary bytes).
36    #[error("Misaligned: length {len} is not a multiple of {unit}.")]
37    Misaligned {
38        /// The offending input length.
39        len: usize,
40        /// The required alignment unit (3 or 4).
41        unit: usize,
42    },
43}
44
45#[cfg(test)]
46mod tests {
47    use super::*;
48
49    // --- Display formatting ---
50
51    #[test]
52    fn integer_overflow_display() {
53        let err = Error::IntegerOverflow;
54        let msg = err.to_string();
55        assert!(
56            msg.contains("Integer Overflow"),
57            "expected 'Integer Overflow' in: {msg}"
58        );
59    }
60
61    #[test]
62    fn misaligned_display_names_len_and_unit() {
63        let err = Error::Misaligned { len: 3, unit: 4 };
64        let msg = err.to_string();
65        assert!(msg.contains('3') && msg.contains('4'), "got: {msg}");
66    }
67
68    #[test]
69    fn invalid_base64_char_display_contains_char() {
70        let err = Error::InvalidBase64Char('+');
71        let msg = err.to_string();
72        assert!(msg.contains('+'), "expected '+' in: {msg}");
73    }
74
75    #[test]
76    fn invalid_base64_char_display_contains_label() {
77        let err = Error::InvalidBase64Char('!');
78        let msg = err.to_string();
79        assert!(
80            msg.contains("Invalid Base64 Character"),
81            "expected 'Invalid Base64 Character' in: {msg}"
82        );
83    }
84
85    #[test]
86    fn invalid_base64_value_display_contains_value() {
87        let err = Error::InvalidBase64Value(65);
88        let msg = err.to_string();
89        assert!(msg.contains("65"), "expected '65' in: {msg}");
90    }
91
92    #[test]
93    fn invalid_base64_value_display_contains_label() {
94        let err = Error::InvalidBase64Value(99);
95        let msg = err.to_string();
96        assert!(
97            msg.contains("Invalid Base64 Value"),
98            "expected 'Invalid Base64 Value' in: {msg}"
99        );
100    }
101
102    #[test]
103    fn short_binary_stream_display() {
104        let err = Error::ShortBinaryStream;
105        let msg = err.to_string();
106        assert!(
107            msg.contains("Short Binary Stream"),
108            "expected 'Short Binary Stream' in: {msg}"
109        );
110    }
111
112    // --- PartialEq / Eq ---
113
114    #[test]
115    fn same_variants_are_equal() {
116        assert_eq!(Error::IntegerOverflow, Error::IntegerOverflow);
117        assert_eq!(Error::ShortBinaryStream, Error::ShortBinaryStream);
118        assert_eq!(Error::InvalidBase64Char('x'), Error::InvalidBase64Char('x'));
119        assert_eq!(Error::InvalidBase64Value(10), Error::InvalidBase64Value(10));
120    }
121
122    #[test]
123    fn different_payloads_are_not_equal() {
124        assert_ne!(Error::InvalidBase64Char('x'), Error::InvalidBase64Char('y'));
125        assert_ne!(Error::InvalidBase64Value(10), Error::InvalidBase64Value(11));
126    }
127
128    #[test]
129    fn different_variants_are_not_equal() {
130        assert_ne!(Error::IntegerOverflow, Error::ShortBinaryStream);
131    }
132
133    // --- Debug ---
134
135    #[test]
136    fn error_debug_contains_variant_name() {
137        let cases: Vec<(&str, Error)> = vec![
138            ("IntegerOverflow", Error::IntegerOverflow),
139            ("InvalidBase64Char", Error::InvalidBase64Char('!')),
140            ("InvalidBase64Value", Error::InvalidBase64Value(99)),
141            ("ShortBinaryStream", Error::ShortBinaryStream),
142        ];
143        for (expected_name, err) in cases {
144            let debug = format!("{err:?}");
145            assert!(
146                debug.contains(expected_name),
147                "expected '{expected_name}' in debug output: {debug}"
148            );
149        }
150    }
151
152    // --- core::error::Error trait ---
153
154    #[test]
155    fn error_implements_std_error() {
156        fn assert_std_error<T: core::error::Error>() {}
157        assert_std_error::<Error>();
158    }
159}