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, Debug, 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
106#[derive(Clone, Copy, Debug, Eq, PartialEq)]
111#[non_exhaustive]
112pub enum DecodeErrorKind {
113 InvalidInput,
116 InvalidLength,
118 InvalidByte,
120 InvalidPadding,
122 InvalidLineWrap,
124 OutputTooSmall,
126 StagingTooSmall,
128}
129
130impl DecodeErrorKind {
131 #[must_use]
133 pub const fn as_str(self) -> &'static str {
134 match self {
135 Self::InvalidInput => "invalid-input",
136 Self::InvalidLength => "invalid-length",
137 Self::InvalidByte => "invalid-byte",
138 Self::InvalidPadding => "invalid-padding",
139 Self::InvalidLineWrap => "invalid-line-wrap",
140 Self::OutputTooSmall => "output-too-small",
141 Self::StagingTooSmall => "staging-too-small",
142 }
143 }
144}
145
146impl core::fmt::Display for DecodeErrorKind {
147 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
148 f.write_str(self.as_str())
149 }
150}
151
152impl core::fmt::Display for DecodeError {
153 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
154 match self {
155 Self::InvalidInput => f.write_str("malformed base64 input"),
156 Self::InvalidLength => f.write_str("invalid base64 input length"),
157 Self::InvalidByte { index, byte } => {
158 write!(f, "invalid base64 byte 0x{byte:02x} at index {index}")
159 }
160 Self::InvalidPadding { index } => write!(f, "invalid base64 padding at index {index}"),
161 Self::InvalidLineWrap { index } => {
162 write!(f, "invalid base64 line wrapping at index {index}")
163 }
164 Self::OutputTooSmall {
165 required,
166 available,
167 } => write!(
168 f,
169 "base64 decode output buffer too small: required {required}, available {available}"
170 ),
171 Self::StagingTooSmall {
172 required,
173 available,
174 } => write!(
175 f,
176 "base64 decode staging buffer too small: required {required}, available {available}"
177 ),
178 }
179 }
180}
181
182impl DecodeError {
183 #[must_use]
190 pub const fn kind(self) -> DecodeErrorKind {
191 match self {
192 Self::InvalidInput => DecodeErrorKind::InvalidInput,
193 Self::InvalidLength => DecodeErrorKind::InvalidLength,
194 Self::InvalidByte { .. } => DecodeErrorKind::InvalidByte,
195 Self::InvalidPadding { .. } => DecodeErrorKind::InvalidPadding,
196 Self::InvalidLineWrap { .. } => DecodeErrorKind::InvalidLineWrap,
197 Self::OutputTooSmall { .. } => DecodeErrorKind::OutputTooSmall,
198 Self::StagingTooSmall { .. } => DecodeErrorKind::StagingTooSmall,
199 }
200 }
201
202 pub(crate) fn with_index_offset(self, offset: usize) -> Self {
203 match self {
204 Self::InvalidByte { index, byte } => Self::InvalidByte {
205 index: index.saturating_add(offset),
206 byte,
207 },
208 Self::InvalidPadding { index } => Self::InvalidPadding {
209 index: index.saturating_add(offset),
210 },
211 Self::InvalidLineWrap { index } => Self::InvalidLineWrap {
212 index: index.saturating_add(offset),
213 },
214 Self::InvalidInput
215 | Self::InvalidLength
216 | Self::OutputTooSmall { .. }
217 | Self::StagingTooSmall { .. } => self,
218 }
219 }
220}
221
222#[cfg(feature = "std")]
223impl std::error::Error for DecodeError {}
224
225#[cfg(test)]
226mod tests {
227 use super::DecodeError;
228
229 #[test]
230 fn index_offsets_saturate_on_overflow() {
231 assert_eq!(
232 DecodeError::InvalidByte {
233 index: 7,
234 byte: b'$'
235 }
236 .with_index_offset(usize::MAX),
237 DecodeError::InvalidByte {
238 index: usize::MAX,
239 byte: b'$'
240 }
241 );
242 assert_eq!(
243 DecodeError::InvalidPadding { index: 7 }.with_index_offset(usize::MAX),
244 DecodeError::InvalidPadding { index: usize::MAX }
245 );
246 assert_eq!(
247 DecodeError::InvalidLineWrap { index: 7 }.with_index_offset(usize::MAX),
248 DecodeError::InvalidLineWrap { index: usize::MAX }
249 );
250 }
251}