#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use core::fmt;
use core::ops::{Deref, DerefMut};
use serde::{Deserializer, Serializer, de};
use crate::alphabet::Config;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[repr(transparent)]
pub struct Base64<T>(pub T);
impl<T> Base64<T> {
pub const fn new(value: T) -> Self {
Self(value)
}
pub fn into_inner(self) -> T {
self.0
}
}
impl<T> Deref for Base64<T> {
type Target = T;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> DerefMut for Base64<T> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<T> AsRef<T> for Base64<T> {
#[inline(always)]
fn as_ref(&self) -> &T {
&self.0
}
}
impl<T> AsMut<T> for Base64<T> {
#[inline(always)]
fn as_mut(&mut self) -> &mut T {
&mut self.0
}
}
impl<T> From<T> for Base64<T> {
#[inline(always)]
fn from(value: T) -> Self {
Self(value)
}
}
impl<T: AsRef<[u8]>> serde::Serialize for Base64<T> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let display = crate::display::display_config(self.0.as_ref(), &Config::STANDARD);
serializer.collect_str(&display)
}
}
#[cfg(feature = "alloc")]
impl<'de> serde::Deserialize<'de> for Base64<Vec<u8>> {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
standard::deserialize(deserializer).map(Base64)
}
}
macro_rules! make_serde_helper {
($mod_name:ident, $config:expr, $doc:expr) => {
#[doc = $doc]
pub mod $mod_name {
use super::*;
pub fn serialize<T, S>(bytes: &T, serializer: S) -> Result<S::Ok, S::Error>
where
T: ?Sized + AsRef<[u8]>,
S: Serializer,
{
let display = crate::display::display_config(bytes.as_ref(), &$config);
serializer.collect_str(&display)
}
#[cfg(feature = "alloc")]
pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
where
D: Deserializer<'de>,
T: TryFrom<Vec<u8>>,
T::Error: fmt::Display,
{
struct Base64Visitor<T>(core::marker::PhantomData<T>);
impl<'de, T> de::Visitor<'de> for Base64Visitor<T>
where
T: TryFrom<Vec<u8>>,
T::Error: fmt::Display,
{
type Value = T;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "a valid Base64 encoded string")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
let bytes = crate::decode::decode(v.as_bytes(), &$config)
.map_err(de::Error::custom)?;
T::try_from(bytes).map_err(de::Error::custom)
}
}
deserializer.deserialize_str(Base64Visitor(core::marker::PhantomData))
}
}
};
}
make_serde_helper!(
standard,
Config::STANDARD,
"Serde adapter for RFC 4648 Standard Base64."
);
make_serde_helper!(
standard_no_pad,
Config::STANDARD_NO_PAD,
"Serde adapter for RFC 4648 Standard Base64 without '=' padding."
);
make_serde_helper!(
url_safe,
Config::URL_SAFE,
"Serde adapter for RFC 4648 URL-safe Base64."
);
make_serde_helper!(
url_safe_no_pad,
Config::URL_SAFE_NO_PAD,
"Serde adapter for RFC 4648 URL-safe Base64 without '=' padding."
);
#[cfg(all(test, feature = "serde", feature = "alloc"))]
mod tests {
use super::*;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct TestPayload {
#[serde(with = "standard")]
pub data: Vec<u8>,
#[serde(with = "url_safe_no_pad")]
pub token: Vec<u8>,
pub wrapped: Base64<Vec<u8>>,
}
#[test]
fn test_serde_roundtrip() {
let payload = TestPayload {
data: b"hello standard".to_vec(),
token: b"url-safe-bytes".to_vec(),
wrapped: Base64(b"wrapped-bytes".to_vec()),
};
let mut ser_output = alloc::string::String::new();
let disp = crate::display::display_config(&payload.data, &Config::STANDARD);
use core::fmt::Write;
write!(ser_output, "{}", disp).unwrap();
assert_eq!(ser_output, "aGVsbG8gc3RhbmRhcmQ=");
}
}