1#[derive(Clone, Copy, Debug, Eq, PartialEq)]
5pub enum EncodeError {
6 LengthOverflow,
8 InvalidLineWrap {
10 line_len: usize,
12 },
13 InputTooLarge {
15 input_len: usize,
17 buffer_len: usize,
19 },
20 OutputTooSmall {
22 required: usize,
24 available: usize,
26 },
27}
28
29impl core::fmt::Display for EncodeError {
30 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
31 match self {
32 Self::LengthOverflow => f.write_str("base64 output length overflows usize"),
33 Self::InvalidLineWrap { line_len } => {
34 write!(f, "base64 line wrap length {line_len} is invalid")
35 }
36 Self::InputTooLarge {
37 input_len,
38 buffer_len,
39 } => write!(
40 f,
41 "base64 input length {input_len} exceeds buffer length {buffer_len}"
42 ),
43 Self::OutputTooSmall {
44 required,
45 available,
46 } => write!(
47 f,
48 "base64 output buffer too small: required {required}, available {available}"
49 ),
50 }
51 }
52}
53
54#[cfg(feature = "std")]
55impl std::error::Error for EncodeError {}
56
57#[derive(Clone, Copy, Eq, PartialEq)]
75pub enum DecodeError {
76 InvalidInput,
79 InvalidLength,
81 InvalidByte {
83 index: usize,
85 byte: u8,
87 },
88 InvalidPadding {
90 index: usize,
92 },
93 InvalidLineWrap {
95 index: usize,
97 },
98 OutputTooSmall {
100 required: usize,
102 available: usize,
104 },
105 StagingTooSmall {
107 required: usize,
109 available: usize,
111 },
112}
113
114impl core::fmt::Debug for DecodeError {
115 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
116 f.debug_struct("DecodeError")
117 .field("kind", &self.kind())
118 .finish_non_exhaustive()
119 }
120}
121
122#[derive(Clone, Copy, Debug, Eq, PartialEq)]
127#[non_exhaustive]
128pub enum DecodeErrorKind {
129 InvalidInput,
132 InvalidLength,
134 InvalidByte,
136 InvalidPadding,
138 InvalidLineWrap,
140 OutputTooSmall,
142 StagingTooSmall,
144}
145
146impl DecodeErrorKind {
147 #[must_use]
149 pub const fn as_str(self) -> &'static str {
150 match self {
151 Self::InvalidInput => "invalid-input",
152 Self::InvalidLength => "invalid-length",
153 Self::InvalidByte => "invalid-byte",
154 Self::InvalidPadding => "invalid-padding",
155 Self::InvalidLineWrap => "invalid-line-wrap",
156 Self::OutputTooSmall => "output-too-small",
157 Self::StagingTooSmall => "staging-too-small",
158 }
159 }
160}
161
162impl core::fmt::Display for DecodeErrorKind {
163 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
164 f.write_str(self.as_str())
165 }
166}
167
168impl core::fmt::Display for DecodeError {
169 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
170 match self {
171 Self::InvalidInput => f.write_str("malformed base64 input"),
172 Self::InvalidLength => f.write_str("invalid base64 input length"),
173 Self::InvalidByte { index, byte } => {
174 write!(f, "invalid base64 byte 0x{byte:02x} at index {index}")
175 }
176 Self::InvalidPadding { index } => write!(f, "invalid base64 padding at index {index}"),
177 Self::InvalidLineWrap { index } => {
178 write!(f, "invalid base64 line wrapping at index {index}")
179 }
180 Self::OutputTooSmall {
181 required,
182 available,
183 } => write!(
184 f,
185 "base64 decode output buffer too small: required {required}, available {available}"
186 ),
187 Self::StagingTooSmall {
188 required,
189 available,
190 } => write!(
191 f,
192 "base64 decode staging buffer too small: required {required}, available {available}"
193 ),
194 }
195 }
196}
197
198impl DecodeError {
199 #[must_use]
206 pub const fn kind(self) -> DecodeErrorKind {
207 match self {
208 Self::InvalidInput => DecodeErrorKind::InvalidInput,
209 Self::InvalidLength => DecodeErrorKind::InvalidLength,
210 Self::InvalidByte { .. } => DecodeErrorKind::InvalidByte,
211 Self::InvalidPadding { .. } => DecodeErrorKind::InvalidPadding,
212 Self::InvalidLineWrap { .. } => DecodeErrorKind::InvalidLineWrap,
213 Self::OutputTooSmall { .. } => DecodeErrorKind::OutputTooSmall,
214 Self::StagingTooSmall { .. } => DecodeErrorKind::StagingTooSmall,
215 }
216 }
217
218 pub(crate) fn with_index_offset(self, offset: usize) -> Self {
219 match self {
220 Self::InvalidByte { index, byte } => Self::InvalidByte {
221 index: index.saturating_add(offset),
222 byte,
223 },
224 Self::InvalidPadding { index } => Self::InvalidPadding {
225 index: index.saturating_add(offset),
226 },
227 Self::InvalidLineWrap { index } => Self::InvalidLineWrap {
228 index: index.saturating_add(offset),
229 },
230 Self::InvalidInput
231 | Self::InvalidLength
232 | Self::OutputTooSmall { .. }
233 | Self::StagingTooSmall { .. } => self,
234 }
235 }
236}
237
238#[cfg(feature = "std")]
239impl std::error::Error for DecodeError {}
240
241#[cfg(test)]
242mod tests {
243 use super::DecodeError;
244
245 #[test]
246 fn index_offsets_saturate_on_overflow() {
247 assert_eq!(
248 DecodeError::InvalidByte {
249 index: 7,
250 byte: b'$'
251 }
252 .with_index_offset(usize::MAX),
253 DecodeError::InvalidByte {
254 index: usize::MAX,
255 byte: b'$'
256 }
257 );
258 assert_eq!(
259 DecodeError::InvalidPadding { index: 7 }.with_index_offset(usize::MAX),
260 DecodeError::InvalidPadding { index: usize::MAX }
261 );
262 assert_eq!(
263 DecodeError::InvalidLineWrap { index: 7 }.with_index_offset(usize::MAX),
264 DecodeError::InvalidLineWrap { index: usize::MAX }
265 );
266 }
267
268 #[cfg(feature = "alloc")]
269 #[test]
270 fn debug_redacts_input_derived_details() {
271 let error = DecodeError::InvalidByte {
272 index: 42,
273 byte: b'$',
274 };
275 let rendered = alloc::format!("{error:?}");
276 assert!(rendered.contains("InvalidByte"));
277 assert!(!rendered.contains("42"));
278 assert!(!rendered.contains("24"));
279 }
280}