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)]
67pub enum DecodeError {
68 InvalidInput,
71 InvalidLength,
73 InvalidByte {
75 index: usize,
77 byte: u8,
79 },
80 InvalidPadding {
82 index: usize,
84 },
85 InvalidLineWrap {
87 index: usize,
89 },
90 OutputTooSmall {
92 required: usize,
94 available: usize,
96 },
97 StagingTooSmall {
99 required: usize,
101 available: usize,
103 },
104}
105
106impl core::fmt::Debug for DecodeError {
107 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
108 f.debug_struct("DecodeError")
109 .field("kind", &self.kind())
110 .finish_non_exhaustive()
111 }
112}
113
114#[derive(Clone, Copy, Debug, Eq, PartialEq)]
119#[non_exhaustive]
120pub enum DecodeErrorKind {
121 InvalidInput,
124 InvalidLength,
126 InvalidByte,
128 InvalidPadding,
130 InvalidLineWrap,
132 OutputTooSmall,
134 StagingTooSmall,
136}
137
138impl DecodeErrorKind {
139 #[must_use]
141 pub const fn as_str(self) -> &'static str {
142 match self {
143 Self::InvalidInput => "invalid-input",
144 Self::InvalidLength => "invalid-length",
145 Self::InvalidByte => "invalid-byte",
146 Self::InvalidPadding => "invalid-padding",
147 Self::InvalidLineWrap => "invalid-line-wrap",
148 Self::OutputTooSmall => "output-too-small",
149 Self::StagingTooSmall => "staging-too-small",
150 }
151 }
152}
153
154impl core::fmt::Display for DecodeErrorKind {
155 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
156 f.write_str(self.as_str())
157 }
158}
159
160impl core::fmt::Display for DecodeError {
161 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
162 match self {
163 Self::InvalidInput => f.write_str("malformed base64 input"),
164 Self::InvalidLength => f.write_str("invalid base64 input length"),
165 Self::InvalidByte { index, byte } => {
166 write!(f, "invalid base64 byte 0x{byte:02x} at index {index}")
167 }
168 Self::InvalidPadding { index } => write!(f, "invalid base64 padding at index {index}"),
169 Self::InvalidLineWrap { index } => {
170 write!(f, "invalid base64 line wrapping at index {index}")
171 }
172 Self::OutputTooSmall {
173 required,
174 available,
175 } => write!(
176 f,
177 "base64 decode output buffer too small: required {required}, available {available}"
178 ),
179 Self::StagingTooSmall {
180 required,
181 available,
182 } => write!(
183 f,
184 "base64 decode staging buffer too small: required {required}, available {available}"
185 ),
186 }
187 }
188}
189
190impl DecodeError {
191 #[must_use]
198 pub const fn kind(self) -> DecodeErrorKind {
199 match self {
200 Self::InvalidInput => DecodeErrorKind::InvalidInput,
201 Self::InvalidLength => DecodeErrorKind::InvalidLength,
202 Self::InvalidByte { .. } => DecodeErrorKind::InvalidByte,
203 Self::InvalidPadding { .. } => DecodeErrorKind::InvalidPadding,
204 Self::InvalidLineWrap { .. } => DecodeErrorKind::InvalidLineWrap,
205 Self::OutputTooSmall { .. } => DecodeErrorKind::OutputTooSmall,
206 Self::StagingTooSmall { .. } => DecodeErrorKind::StagingTooSmall,
207 }
208 }
209
210 pub(crate) fn with_index_offset(self, offset: usize) -> Self {
211 match self {
212 Self::InvalidByte { index, byte } => Self::InvalidByte {
213 index: index.saturating_add(offset),
214 byte,
215 },
216 Self::InvalidPadding { index } => Self::InvalidPadding {
217 index: index.saturating_add(offset),
218 },
219 Self::InvalidLineWrap { index } => Self::InvalidLineWrap {
220 index: index.saturating_add(offset),
221 },
222 Self::InvalidInput
223 | Self::InvalidLength
224 | Self::OutputTooSmall { .. }
225 | Self::StagingTooSmall { .. } => self,
226 }
227 }
228}
229
230#[cfg(feature = "std")]
231impl std::error::Error for DecodeError {}
232
233#[cfg(test)]
234mod tests {
235 use super::DecodeError;
236
237 #[test]
238 fn index_offsets_saturate_on_overflow() {
239 assert_eq!(
240 DecodeError::InvalidByte {
241 index: 7,
242 byte: b'$'
243 }
244 .with_index_offset(usize::MAX),
245 DecodeError::InvalidByte {
246 index: usize::MAX,
247 byte: b'$'
248 }
249 );
250 assert_eq!(
251 DecodeError::InvalidPadding { index: 7 }.with_index_offset(usize::MAX),
252 DecodeError::InvalidPadding { index: usize::MAX }
253 );
254 assert_eq!(
255 DecodeError::InvalidLineWrap { index: 7 }.with_index_offset(usize::MAX),
256 DecodeError::InvalidLineWrap { index: usize::MAX }
257 );
258 }
259
260 #[cfg(feature = "alloc")]
261 #[test]
262 fn debug_redacts_input_derived_details() {
263 let error = DecodeError::InvalidByte {
264 index: 42,
265 byte: b'$',
266 };
267 let rendered = alloc::format!("{error:?}");
268 assert!(rendered.contains("InvalidByte"));
269 assert!(!rendered.contains("42"));
270 assert!(!rendered.contains("24"));
271 }
272}