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}