use crate::{formats, DeserializeAs, SerializeAs};
use alloc::vec::Vec;
use base64::Config;
use core::{
convert::{TryFrom, TryInto},
fmt,
marker::PhantomData,
};
use serde::{de::Error, Deserializer, Serialize, Serializer};
pub struct Base64<CHARSET: CharacterSet = Standard, PADDING: formats::Format = formats::Padded>(
PhantomData<(CHARSET, PADDING)>,
);
impl<T, CHARSET> SerializeAs<T> for Base64<CHARSET, formats::Padded>
where
T: AsRef<[u8]>,
CHARSET: CharacterSet,
{
fn serialize_as<S>(source: &T, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
base64::encode_config(source, base64::Config::new(CHARSET::charset(), true))
.serialize(serializer)
}
}
impl<T, CHARSET> SerializeAs<T> for Base64<CHARSET, formats::Unpadded>
where
T: AsRef<[u8]>,
CHARSET: CharacterSet,
{
fn serialize_as<S>(source: &T, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
base64::encode_config(source, base64::Config::new(CHARSET::charset(), false))
.serialize(serializer)
}
}
impl<'de, T, CHARSET, FORMAT> DeserializeAs<'de, T> for Base64<CHARSET, FORMAT>
where
T: TryFrom<Vec<u8>>,
CHARSET: CharacterSet,
FORMAT: formats::Format,
{
fn deserialize_as<D>(deserializer: D) -> Result<T, D::Error>
where
D: Deserializer<'de>,
{
struct Visitor<T, CHARSET>(PhantomData<(T, CHARSET)>);
impl<'de, T, CHARSET> serde::de::Visitor<'de> for Visitor<T, CHARSET>
where
T: TryFrom<Vec<u8>>,
CHARSET: CharacterSet,
{
type Value = T;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a base64 encoded string")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: Error,
{
let bytes = base64::decode_config(value, Config::new(CHARSET::charset(), false))
.map_err(Error::custom)?;
let length = bytes.len();
bytes.try_into().map_err(|_e: T::Error| {
Error::custom(format_args!(
"Can't convert a Byte Vector of length {} to the output type.",
length
))
})
}
}
deserializer.deserialize_str(Visitor::<T, CHARSET>(PhantomData))
}
}
pub trait CharacterSet {
fn charset() -> base64::CharacterSet;
}
pub struct Standard;
impl CharacterSet for Standard {
fn charset() -> base64::CharacterSet {
base64::CharacterSet::Standard
}
}
pub struct UrlSafe;
impl CharacterSet for UrlSafe {
fn charset() -> base64::CharacterSet {
base64::CharacterSet::UrlSafe
}
}
pub struct Crypt;
impl CharacterSet for Crypt {
fn charset() -> base64::CharacterSet {
base64::CharacterSet::Crypt
}
}
pub struct Bcrypt;
impl CharacterSet for Bcrypt {
fn charset() -> base64::CharacterSet {
base64::CharacterSet::Bcrypt
}
}
pub struct ImapMutf7;
impl CharacterSet for ImapMutf7 {
fn charset() -> base64::CharacterSet {
base64::CharacterSet::ImapMutf7
}
}
pub struct BinHex;
impl CharacterSet for BinHex {
fn charset() -> base64::CharacterSet {
base64::CharacterSet::BinHex
}
}