const_base/
errors.rs

1//! All the errors from this crate.
2
3use crate::Encoding;
4
5/// Error returned by [`decode`](crate::decode())
6#[derive(Debug, PartialEq)]
7#[non_exhaustive]
8pub enum DecodeError {
9    /// When one of the bytes in the slice passed to [`decode`]
10    /// isn't in the char set of the passed encoding.
11    ///
12    /// Eg: a `!` in an otherwise base 64 encoded string.
13    ///
14    /// [`decode`]: crate::decode()
15    InvalidByte(InvalidByte),
16
17    /// When the array returned by [`decode`]
18    /// isn't the length that the arguments would produce.
19    ///
20    /// [`decode`]: crate::decode()
21    WrongOutputLength(WrongOutputLength),
22    /// When the slice passed to [`decode`] is not a valid length for that encoding.
23    ///
24    #[doc = wrong_lengths_doc!()]
25    WrongInputLength(WrongInputLength),
26    /// When the last byte in the slice passed to [`decode`]
27    /// has excess set bits that aren't copied to the return value.
28    ExcessBits(ExcessBits),
29}
30
31macro_rules! define_unwrap_self {
32    () => {
33        /// Unwraps a Result with this type as the error.
34        pub const fn unwrap<T: Copy>(res: Result<T, Self>) -> T {
35            match res {
36                Ok(x) => x,
37                Err(e) => e.panic(),
38            }
39        }
40    };
41}
42
43impl DecodeError {
44    define_unwrap_self! {}
45
46    /// Panics with this error as the message.
47    #[track_caller]
48    pub const fn panic(&self) -> ! {
49        match self {
50            DecodeError::InvalidByte(x) => x.panic(),
51            DecodeError::WrongOutputLength(x) => x.panic(),
52            DecodeError::WrongInputLength(x) => x.panic(),
53            DecodeError::ExcessBits(x) => x.panic(),
54        }
55    }
56}
57
58/// When one of the bytes in the slice passed to [`decode`]
59/// isn't in the char set of the passed encoding.
60///
61/// Eg: a `!` in an otherwise base 64 encoded string.
62///
63/// [`decode`]: crate::decode()
64///
65/// # Example
66///
67/// ### Base 64
68///
69/// ```rust
70/// use const_base::{Config, DecodeError, InvalidByte, decode};
71///
72/// const DECODED: Result<[u8; 4], DecodeError> = decode(b"bGl!ZQ", Config::B64);
73///
74/// assert!(matches!(DECODED, Err(DecodeError::InvalidByte(InvalidByte{..}))));
75///
76/// ```
77#[derive(Debug, PartialEq)]
78pub struct InvalidByte {
79    pub(crate) index: usize,
80    pub(crate) byte: u8,
81    pub(crate) as_char: char,
82    pub(crate) encoding: Encoding,
83}
84
85impl InvalidByte {
86    pub const fn index(&self) -> usize {
87        self.index
88    }
89    pub const fn byte(&self) -> u8 {
90        self.byte
91    }
92    pub const fn byte_as_char(&self) -> char {
93        self.as_char
94    }
95    pub const fn encoding(&self) -> Encoding {
96        self.encoding
97    }
98
99    define_unwrap_self! {}
100
101    /// Panics with this error as the message.
102    #[track_caller]
103    pub const fn panic(&self) -> ! {
104        use const_panic::{FmtArg, PanicVal};
105
106        crate::utils::cpanic(&[
107            PanicVal::write_str("invalid byte ("),
108            PanicVal::from_u8(self.byte, FmtArg::DEBUG),
109            PanicVal::write_str("_u8, the "),
110            PanicVal::from_char(self.as_char, FmtArg::DEBUG),
111            PanicVal::write_str(" character) for the "),
112            PanicVal::write_str(self.encoding.full_name()),
113            PanicVal::write_str(" encoding at offset "),
114            PanicVal::from_usize(self.index, FmtArg::DEBUG),
115        ])
116    }
117}
118
119/// When the array returned by [`decode`] or [`encode`] isn't the
120/// length that the arguments would produce.
121///
122/// [`decode`]: crate::decode()
123/// [`encode`]: crate::encode()
124///
125/// # Example
126///
127/// ### Base 64
128///
129/// ```rust
130/// use const_base::{Config, DecodeError, WrongOutputLength, decode};
131///
132/// const DECODED: Result<[u8; 8], DecodeError> = decode(b"AAAAAA", Config::B64);
133///
134/// match DECODED {
135///     Err(DecodeError::WrongOutputLength(err)) => {
136///         assert_eq!(err.expected(), 4);
137///         assert_eq!(err.found(), 8);
138///     }
139///     _ => unreachable!()
140/// }
141///
142/// ```
143///
144/// [`decode`]: crate::decode()
145#[derive(Debug, PartialEq)]
146pub struct WrongOutputLength {
147    pub(crate) expected: usize,
148    pub(crate) found: usize,
149}
150
151impl WrongOutputLength {
152    pub const fn expected(&self) -> usize {
153        self.expected
154    }
155    pub const fn found(&self) -> usize {
156        self.found
157    }
158
159    define_unwrap_self! {}
160
161    /// Panics with this error as the message.
162    #[track_caller]
163    pub const fn panic(&self) -> ! {
164        use const_panic::{FmtArg, PanicVal};
165
166        crate::utils::cpanic(&[
167            PanicVal::write_str("expected output length to be "),
168            PanicVal::from_usize(self.expected, FmtArg::DEBUG),
169            PanicVal::write_str(" but it is "),
170            PanicVal::from_usize(self.found, FmtArg::DEBUG),
171        ])
172    }
173}
174
175macro_rules! wrong_lengths_doc {
176    () => {
177        "The input lengths that are wrong for each encoding:\n\
178        - Base 64: when `input.len() % 4` equals `1`.\n\
179        - Base 32: when `input.len() % 8` equals `1`, `3` , or `6`.\n\
180        - Base 16: when `input.len() % 2` equals `1`.\n\
181        "
182    };
183}
184use wrong_lengths_doc;
185
186/// When the slice passed to [`decode`] is not a valid length for the passed encoding.
187///
188#[doc = wrong_lengths_doc!()]
189///
190/// [`decode`]: crate::decode()
191///
192/// # Example
193///
194/// ### Base 64
195///
196/// ```rust
197/// use const_base::{Config, DecodeError, Encoding, WrongInputLength, decode};
198///
199/// const DECODED: Result<[u8; 8], DecodeError> = decode(b"AAAAA", Config::B64);
200///
201/// match DECODED {
202///     Err(DecodeError::WrongInputLength(err)) => {
203///         assert_eq!(err.length(), 5);
204///         assert!(matches!(err.encoding(), Encoding::Base64{..}));
205///     }
206///     _ => unreachable!()
207/// }
208///
209/// ```
210///
211/// [`decode`]: crate::decode()
212#[derive(Debug, PartialEq)]
213pub struct WrongInputLength {
214    pub(crate) length: usize,
215    pub(crate) enc: Encoding,
216}
217
218impl WrongInputLength {
219    /// The length of the slice argument
220    pub const fn length(&self) -> usize {
221        self.length
222    }
223
224    /// The encoding that was attempted to decode from.
225    pub const fn encoding(&self) -> Encoding {
226        self.enc
227    }
228
229    define_unwrap_self! {}
230
231    /// Panics with this error as the message.
232    #[track_caller]
233    pub const fn panic(&self) -> ! {
234        use const_panic::{FmtArg, PanicVal};
235
236        crate::utils::cpanic(&[
237            PanicVal::write_str("invalid input length for "),
238            PanicVal::write_str(self.enc.name()),
239            PanicVal::write_str(": "),
240            PanicVal::from_usize(self.length, FmtArg::DEBUG),
241        ])
242    }
243}
244
245/// When the last byte in the slice passed to [`decode`]
246/// has excess set bits that aren't copied to the return value.
247///
248/// # Example
249///
250/// ```rust
251/// use const_base::{Config, DecodeError, decode};
252///
253/// assert_eq!(decode::<2>(b"ABA", Config::B64).unwrap(), [0u8, 16]);
254///
255/// // base 64 inputs of length 3 are 18 bits, which is 2 bytes and 2 excess bits.
256/// // `ABC` is base64 for `[0b00000000, 0b00010000]` with excess `0b10` bits.
257/// //
258/// // Because the two unused bits at the end (which are `10`) include a set bit,
259/// // it causes the `ExcessBits` error.
260/// match decode::<2>(b"ABC", Config::B64) {
261///     Err(DecodeError::ExcessBits(err)) => {
262///         assert_eq!(err.last_byte(), b'C');
263///     }
264///     _ => unreachable!()
265/// }
266/// ```
267#[derive(Debug, PartialEq)]
268pub struct ExcessBits {
269    pub(crate) last_byte: u8,
270}
271
272impl ExcessBits {
273    pub const fn last_byte(&self) -> u8 {
274        self.last_byte
275    }
276
277    define_unwrap_self! {}
278
279    /// Panics with this error as the message.
280    #[track_caller]
281    pub const fn panic(&self) -> ! {
282        use const_panic::{FmtArg, PanicVal};
283
284        crate::utils::cpanic(&[
285            PanicVal::write_str("excess bits in last byte: "),
286            PanicVal::from_u8(self.last_byte, FmtArg::DEBUG),
287            PanicVal::write_str("_u8 (the "),
288            PanicVal::from_char(self.last_byte as char, FmtArg::DEBUG),
289            PanicVal::write_str(" character)"),
290        ])
291    }
292}
293
294#[doc(hidden)]
295#[track_caller]
296pub const fn __unwrap_encode<const N: usize>(
297    res: Result<crate::ArrayStr<N>, WrongOutputLength>,
298) -> crate::ArrayStr<N> {
299    match res {
300        Ok(x) => x,
301        Err(e) => e.panic(),
302    }
303}