use std::error::Error;
use std::fmt;
use base64::Engine as _;
use base64::engine::general_purpose::{STANDARD, STANDARD_NO_PAD, URL_SAFE, URL_SAFE_NO_PAD};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Base64DecodeError {
InvalidByte {
index: usize,
byte: u8,
},
InvalidLength {
len: usize,
},
InvalidLastSymbol {
index: usize,
byte: u8,
},
InvalidPadding,
}
impl fmt::Display for Base64DecodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidByte { index, byte } => {
write!(
f,
"base64 input contains invalid byte 0x{byte:02x} at position {index}"
)
}
Self::InvalidLength { len } => {
write!(f, "base64 input has invalid byte length {len}")
}
Self::InvalidLastSymbol { index, byte } => {
write!(
f,
"base64 input contains invalid last symbol 0x{byte:02x} at position {index}"
)
}
Self::InvalidPadding => write!(f, "base64 input violates padding policy"),
}
}
}
impl Error for Base64DecodeError {}
impl From<base64::DecodeError> for Base64DecodeError {
fn from(error: base64::DecodeError) -> Self {
match error {
base64::DecodeError::InvalidByte(index, byte) => Self::InvalidByte { index, byte },
base64::DecodeError::InvalidLength(len) => Self::InvalidLength { len },
base64::DecodeError::InvalidLastSymbol(index, byte) => {
Self::InvalidLastSymbol { index, byte }
}
base64::DecodeError::InvalidPadding => Self::InvalidPadding,
}
}
}
#[must_use]
pub fn encode_base64(bytes: impl AsRef<[u8]>) -> String {
STANDARD.encode(bytes)
}
pub fn decode_base64(encoded: impl AsRef<str>) -> Result<Vec<u8>, Base64DecodeError> {
STANDARD.decode(encoded.as_ref()).map_err(Into::into)
}
#[must_use]
pub fn encode_base64_unpadded(bytes: impl AsRef<[u8]>) -> String {
STANDARD_NO_PAD.encode(bytes)
}
pub fn decode_base64_unpadded(encoded: impl AsRef<str>) -> Result<Vec<u8>, Base64DecodeError> {
STANDARD_NO_PAD.decode(encoded.as_ref()).map_err(Into::into)
}
#[must_use]
pub fn encode_base64_url(bytes: impl AsRef<[u8]>) -> String {
URL_SAFE.encode(bytes)
}
pub fn decode_base64_url(encoded: impl AsRef<str>) -> Result<Vec<u8>, Base64DecodeError> {
URL_SAFE.decode(encoded.as_ref()).map_err(Into::into)
}
#[must_use]
pub fn encode_base64_url_unpadded(bytes: impl AsRef<[u8]>) -> String {
URL_SAFE_NO_PAD.encode(bytes)
}
pub fn decode_base64_url_unpadded(encoded: impl AsRef<str>) -> Result<Vec<u8>, Base64DecodeError> {
URL_SAFE_NO_PAD.decode(encoded.as_ref()).map_err(Into::into)
}