Skip to main content

base64_ng/
errors.rs

1//! Error types for encoding and decoding operations.
2
3/// Encoding error.
4#[derive(Clone, Copy, Debug, Eq, PartialEq)]
5pub enum EncodeError {
6    /// The encoded output length would overflow `usize`.
7    LengthOverflow,
8    /// The requested line wrapping policy is invalid.
9    InvalidLineWrap {
10        /// Requested line length.
11        line_len: usize,
12    },
13    /// The caller-provided input length exceeds the provided buffer.
14    InputTooLarge {
15        /// Requested input bytes.
16        input_len: usize,
17        /// Available buffer bytes.
18        buffer_len: usize,
19    },
20    /// The output buffer is too small.
21    OutputTooSmall {
22        /// Required output bytes.
23        required: usize,
24        /// Available output bytes.
25        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/// Decoding error.
58#[derive(Clone, Copy, Debug, Eq, PartialEq)]
59pub enum DecodeError {
60    /// The encoded input is malformed, but the decoder intentionally does not
61    /// disclose a more specific error class.
62    InvalidInput,
63    /// The encoded input length is impossible for the selected padding policy.
64    InvalidLength,
65    /// A byte is not valid for the selected alphabet.
66    InvalidByte {
67        /// Byte index in the input.
68        index: usize,
69        /// Invalid byte value.
70        byte: u8,
71    },
72    /// Padding is missing, misplaced, or non-canonical.
73    InvalidPadding {
74        /// Byte index where padding became invalid.
75        index: usize,
76    },
77    /// Line wrapping is missing, misplaced, or uses the wrong line ending.
78    InvalidLineWrap {
79        /// Byte index where line wrapping became invalid.
80        index: usize,
81    },
82    /// The output buffer is too small.
83    OutputTooSmall {
84        /// Required output bytes.
85        required: usize,
86        /// Available output bytes.
87        available: usize,
88    },
89    /// The caller-provided constant-time staging buffer is too small.
90    StagingTooSmall {
91        /// Required staging bytes.
92        required: usize,
93        /// Available staging bytes.
94        available: usize,
95    },
96}
97
98/// Redacted decoding error class.
99///
100/// This type intentionally omits input-derived bytes and indexes so callers can
101/// log error classes without logging secret-adjacent input content.
102#[derive(Clone, Copy, Debug, Eq, PartialEq)]
103#[non_exhaustive]
104pub enum DecodeErrorKind {
105    /// The encoded input is malformed, but the decoder intentionally does not
106    /// disclose a more specific error class.
107    InvalidInput,
108    /// The encoded input length is impossible for the selected padding policy.
109    InvalidLength,
110    /// A byte is not valid for the selected alphabet.
111    InvalidByte,
112    /// Padding is missing, misplaced, or non-canonical.
113    InvalidPadding,
114    /// Line wrapping is missing, misplaced, or uses the wrong line ending.
115    InvalidLineWrap,
116    /// The output buffer is too small.
117    OutputTooSmall,
118    /// The caller-provided constant-time staging buffer is too small.
119    StagingTooSmall,
120}
121
122impl DecodeErrorKind {
123    /// Returns the stable lowercase identifier for this error class.
124    #[must_use]
125    pub const fn as_str(self) -> &'static str {
126        match self {
127            Self::InvalidInput => "invalid-input",
128            Self::InvalidLength => "invalid-length",
129            Self::InvalidByte => "invalid-byte",
130            Self::InvalidPadding => "invalid-padding",
131            Self::InvalidLineWrap => "invalid-line-wrap",
132            Self::OutputTooSmall => "output-too-small",
133            Self::StagingTooSmall => "staging-too-small",
134        }
135    }
136}
137
138impl core::fmt::Display for DecodeErrorKind {
139    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
140        f.write_str(self.as_str())
141    }
142}
143
144impl core::fmt::Display for DecodeError {
145    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
146        match self {
147            Self::InvalidInput => f.write_str("malformed base64 input"),
148            Self::InvalidLength => f.write_str("invalid base64 input length"),
149            Self::InvalidByte { index, byte } => {
150                write!(f, "invalid base64 byte 0x{byte:02x} at index {index}")
151            }
152            Self::InvalidPadding { index } => write!(f, "invalid base64 padding at index {index}"),
153            Self::InvalidLineWrap { index } => {
154                write!(f, "invalid base64 line wrapping at index {index}")
155            }
156            Self::OutputTooSmall {
157                required,
158                available,
159            } => write!(
160                f,
161                "base64 decode output buffer too small: required {required}, available {available}"
162            ),
163            Self::StagingTooSmall {
164                required,
165                available,
166            } => write!(
167                f,
168                "base64 decode staging buffer too small: required {required}, available {available}"
169            ),
170        }
171    }
172}
173
174impl DecodeError {
175    /// Returns a redacted error class without input-derived bytes or indexes.
176    ///
177    /// Strict decoders keep exact diagnostics in [`DecodeError`] and
178    /// [`core::fmt::Display`] for developer debugging. When input may contain
179    /// secrets or secret-adjacent material, log this kind instead of logging
180    /// the full error value.
181    #[must_use]
182    pub const fn kind(self) -> DecodeErrorKind {
183        match self {
184            Self::InvalidInput => DecodeErrorKind::InvalidInput,
185            Self::InvalidLength => DecodeErrorKind::InvalidLength,
186            Self::InvalidByte { .. } => DecodeErrorKind::InvalidByte,
187            Self::InvalidPadding { .. } => DecodeErrorKind::InvalidPadding,
188            Self::InvalidLineWrap { .. } => DecodeErrorKind::InvalidLineWrap,
189            Self::OutputTooSmall { .. } => DecodeErrorKind::OutputTooSmall,
190            Self::StagingTooSmall { .. } => DecodeErrorKind::StagingTooSmall,
191        }
192    }
193
194    pub(crate) fn with_index_offset(self, offset: usize) -> Self {
195        match self {
196            Self::InvalidByte { index, byte } => Self::InvalidByte {
197                index: index + offset,
198                byte,
199            },
200            Self::InvalidPadding { index } => Self::InvalidPadding {
201                index: index + offset,
202            },
203            Self::InvalidLineWrap { index } => Self::InvalidLineWrap {
204                index: index + offset,
205            },
206            Self::InvalidInput
207            | Self::InvalidLength
208            | Self::OutputTooSmall { .. }
209            | Self::StagingTooSmall { .. } => self,
210        }
211    }
212}
213
214#[cfg(feature = "std")]
215impl std::error::Error for DecodeError {}