use base64::Engine;
use crate::CryptoError;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Encoding {
Standard,
UrlSafeNoPad,
}
impl Encoding {
#[must_use]
pub fn encode(&self, data: &[u8]) -> String {
match self {
Encoding::Standard => base64::engine::general_purpose::STANDARD.encode(data),
Encoding::UrlSafeNoPad => base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(data),
}
}
pub fn decode(&self, data: &str) -> Result<Vec<u8>, base64::DecodeError> {
match self {
Encoding::Standard => base64::engine::general_purpose::STANDARD.decode(data),
Encoding::UrlSafeNoPad => base64::engine::general_purpose::URL_SAFE_NO_PAD.decode(data),
}
}
#[must_use]
pub const fn as_str(&self) -> &'static str {
match self {
Encoding::Standard => "standard",
Encoding::UrlSafeNoPad => "url_safe_no_pad",
}
}
}
impl std::str::FromStr for Encoding {
type Err = CryptoError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"standard" => Ok(Encoding::Standard),
"url_safe_no_pad" => Ok(Encoding::UrlSafeNoPad),
_ => Err(CryptoError::InvalidEncoding),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn encode_known_vectors() {
assert_eq!(Encoding::Standard.encode(b"hello"), "aGVsbG8=");
assert_eq!(Encoding::UrlSafeNoPad.encode(b"hello"), "aGVsbG8");
assert_eq!(Encoding::UrlSafeNoPad.encode(&[0xfb, 0xff]), "-_8");
assert_eq!(Encoding::Standard.encode(&[0xfb, 0xff]), "+/8=");
}
#[test]
fn from_str_accepts_canonical_names() {
assert_eq!("standard".parse::<Encoding>().unwrap(), Encoding::Standard);
assert_eq!(
"url_safe_no_pad".parse::<Encoding>().unwrap(),
Encoding::UrlSafeNoPad
);
}
#[test]
fn from_str_rejects_unknown_names() {
for name in ["", "Standard", "url-safe", "urlsafe-no-pad", "base64"] {
assert_eq!(
name.parse::<Encoding>().unwrap_err(),
CryptoError::InvalidEncoding,
"{name:?} must not parse"
);
}
}
#[test]
fn as_str_roundtrips_through_from_str() {
for encoding in [Encoding::Standard, Encoding::UrlSafeNoPad] {
let parsed = encoding.as_str().parse::<Encoding>().unwrap();
assert_eq!(parsed, encoding, "as_str/from_str must roundtrip");
}
}
}