1#[derive(Clone, Copy, Debug, Eq, PartialEq)]
5pub enum EncodeError {
6 LengthOverflow,
8 InvalidAlphabet,
15 InvalidLineWrap {
17 line_len: usize,
19 },
20 InputTooLarge {
22 input_len: usize,
24 buffer_len: usize,
26 },
27 OutputTooSmall {
29 required: usize,
31 available: usize,
33 },
34}
35
36impl core::fmt::Display for EncodeError {
37 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
38 match self {
39 Self::LengthOverflow => f.write_str("base64 output length overflows usize"),
40 Self::InvalidAlphabet => f.write_str("base64 alphabet produced non-ASCII output"),
41 Self::InvalidLineWrap { line_len } => {
42 write!(f, "base64 line wrap length {line_len} is invalid")
43 }
44 Self::InputTooLarge {
45 input_len,
46 buffer_len,
47 } => write!(
48 f,
49 "base64 input length {input_len} exceeds buffer length {buffer_len}"
50 ),
51 Self::OutputTooSmall {
52 required,
53 available,
54 } => write!(
55 f,
56 "base64 output buffer too small: required {required}, available {available}"
57 ),
58 }
59 }
60}
61
62#[cfg(feature = "std")]
63impl std::error::Error for EncodeError {}
64
65#[derive(Clone, Copy, Eq, PartialEq)]
83pub enum DecodeError {
84 InvalidInput,
87 InvalidLength,
89 InvalidByte {
91 index: usize,
93 byte: u8,
95 },
96 InvalidPadding {
98 index: usize,
100 },
101 InvalidLineWrap {
103 index: usize,
105 },
106 OutputTooSmall {
108 required: usize,
110 available: usize,
112 },
113 StagingTooSmall {
115 required: usize,
117 available: usize,
119 },
120}
121
122impl core::fmt::Debug for DecodeError {
123 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
124 f.debug_struct("DecodeError")
125 .field("kind", &self.kind())
126 .finish_non_exhaustive()
127 }
128}
129
130#[derive(Clone, Copy, Debug, Eq, PartialEq)]
135#[non_exhaustive]
136pub enum DecodeErrorKind {
137 InvalidInput,
140 InvalidLength,
142 InvalidByte,
144 InvalidPadding,
146 InvalidLineWrap,
148 OutputTooSmall,
150 StagingTooSmall,
152}
153
154impl DecodeErrorKind {
155 #[must_use]
157 pub const fn as_str(self) -> &'static str {
158 match self {
159 Self::InvalidInput => "invalid-input",
160 Self::InvalidLength => "invalid-length",
161 Self::InvalidByte => "invalid-byte",
162 Self::InvalidPadding => "invalid-padding",
163 Self::InvalidLineWrap => "invalid-line-wrap",
164 Self::OutputTooSmall => "output-too-small",
165 Self::StagingTooSmall => "staging-too-small",
166 }
167 }
168}
169
170impl core::fmt::Display for DecodeErrorKind {
171 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
172 f.write_str(self.as_str())
173 }
174}
175
176impl core::fmt::Display for DecodeError {
177 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
178 match self {
179 Self::InvalidInput => f.write_str("malformed base64 input"),
180 Self::InvalidLength => f.write_str("invalid base64 input length"),
181 Self::InvalidByte { index, byte } => {
182 write!(f, "invalid base64 byte 0x{byte:02x} at index {index}")
183 }
184 Self::InvalidPadding { index } => write!(f, "invalid base64 padding at index {index}"),
185 Self::InvalidLineWrap { index } => {
186 write!(f, "invalid base64 line wrapping at index {index}")
187 }
188 Self::OutputTooSmall {
189 required,
190 available,
191 } => write!(
192 f,
193 "base64 decode output buffer too small: required {required}, available {available}"
194 ),
195 Self::StagingTooSmall {
196 required,
197 available,
198 } => write!(
199 f,
200 "base64 decode staging buffer too small: required {required}, available {available}"
201 ),
202 }
203 }
204}
205
206impl DecodeError {
207 #[must_use]
214 pub const fn kind(self) -> DecodeErrorKind {
215 match self {
216 Self::InvalidInput => DecodeErrorKind::InvalidInput,
217 Self::InvalidLength => DecodeErrorKind::InvalidLength,
218 Self::InvalidByte { .. } => DecodeErrorKind::InvalidByte,
219 Self::InvalidPadding { .. } => DecodeErrorKind::InvalidPadding,
220 Self::InvalidLineWrap { .. } => DecodeErrorKind::InvalidLineWrap,
221 Self::OutputTooSmall { .. } => DecodeErrorKind::OutputTooSmall,
222 Self::StagingTooSmall { .. } => DecodeErrorKind::StagingTooSmall,
223 }
224 }
225
226 pub(crate) fn with_index_offset(self, offset: usize) -> Self {
227 match self {
228 Self::InvalidByte { index, byte } => Self::InvalidByte {
229 index: index.saturating_add(offset),
230 byte,
231 },
232 Self::InvalidPadding { index } => Self::InvalidPadding {
233 index: index.saturating_add(offset),
234 },
235 Self::InvalidLineWrap { index } => Self::InvalidLineWrap {
236 index: index.saturating_add(offset),
237 },
238 Self::InvalidInput
239 | Self::InvalidLength
240 | Self::OutputTooSmall { .. }
241 | Self::StagingTooSmall { .. } => self,
242 }
243 }
244}
245
246#[cfg(feature = "std")]
247impl std::error::Error for DecodeError {}
248
249#[cfg(test)]
250mod tests {
251 use super::DecodeError;
252
253 #[test]
254 fn index_offsets_saturate_on_overflow() {
255 assert_eq!(
256 DecodeError::InvalidByte {
257 index: 7,
258 byte: b'$'
259 }
260 .with_index_offset(usize::MAX),
261 DecodeError::InvalidByte {
262 index: usize::MAX,
263 byte: b'$'
264 }
265 );
266 assert_eq!(
267 DecodeError::InvalidPadding { index: 7 }.with_index_offset(usize::MAX),
268 DecodeError::InvalidPadding { index: usize::MAX }
269 );
270 assert_eq!(
271 DecodeError::InvalidLineWrap { index: 7 }.with_index_offset(usize::MAX),
272 DecodeError::InvalidLineWrap { index: usize::MAX }
273 );
274 }
275
276 #[cfg(feature = "alloc")]
277 #[test]
278 fn debug_redacts_input_derived_details() {
279 let error = DecodeError::InvalidByte {
280 index: 42,
281 byte: b'$',
282 };
283 let rendered = alloc::format!("{error:?}");
284 assert!(rendered.contains("InvalidByte"));
285 assert!(!rendered.contains("42"));
286 assert!(!rendered.contains("24"));
287 }
288}