1#![deny(missing_docs)]
17#![no_std]
18
19extern crate alloc;
20
21use alloc::vec::Vec;
22use base64::Engine;
23use core::{
24 fmt::{self, Display},
25 marker::PhantomData,
26};
27use serdapt::{DeserializeWith, SerializeWith};
28use serde::{de::Visitor, Deserializer, Serialize, Serializer};
29
30pub struct Base64<A = Standard> {
51 alphabet: PhantomData<A>,
52}
53
54pub type StdBase64 = Base64;
56
57impl<A> Base64<A> {
58 pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
60 where
61 T: ?Sized,
62 S: Serializer,
63 Self: SerializeWith<T>,
64 {
65 Self::serialize_with(value, serializer)
66 }
67
68 pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
70 where
71 D: Deserializer<'de>,
72 Self: DeserializeWith<'de, T>,
73 {
74 Self::deserialize_with(deserializer)
75 }
76}
77
78impl<A, T> SerializeWith<T> for Base64<A>
79where
80 A: AlphabetTag,
81 T: AsRef<[u8]>,
82{
83 fn serialize_with<S: Serializer>(bytes: &T, serializer: S) -> Result<S::Ok, S::Error> {
84 Serialize::serialize(&A::VALUE.inner.encode(bytes), serializer)
85 }
86}
87
88impl<'de, A, T> DeserializeWith<'de, T> for Base64<A>
89where
90 A: AlphabetTag,
91 T: TryFrom<Vec<u8>>,
92 T::Error: Display,
93{
94 fn deserialize_with<D>(deserializer: D) -> Result<T, D::Error>
95 where
96 D: Deserializer<'de>,
97 {
98 let bytes = deserializer.deserialize_str(Base64Visitor::new::<A>())?;
99 bytes.try_into().map_err(serde::de::Error::custom)
100 }
101}
102
103struct Base64Visitor {
104 engine: &'static base64::engine::GeneralPurpose,
105}
106
107impl Base64Visitor {
108 const fn new<A>() -> Self
109 where
110 A: AlphabetTag,
111 {
112 Self {
113 engine: &A::VALUE.inner,
114 }
115 }
116}
117
118impl Visitor<'_> for Base64Visitor {
119 type Value = Vec<u8>;
120
121 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 f.write_str("a base64 string")
123 }
124
125 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
126 where
127 E: serde::de::Error,
128 {
129 self.engine.decode(v).map_err(serde::de::Error::custom)
130 }
131}
132
133pub struct Base64Array<A = Standard> {
152 alphabet: PhantomData<A>,
153}
154
155pub type StdBase64Array = Base64Array;
157
158impl<A> Base64Array<A> {
159 pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
161 where
162 T: ?Sized,
163 S: Serializer,
164 Self: SerializeWith<T>,
165 {
166 Self::serialize_with(value, serializer)
167 }
168
169 pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
171 where
172 D: Deserializer<'de>,
173 Self: DeserializeWith<'de, T>,
174 {
175 Self::deserialize_with(deserializer)
176 }
177}
178
179impl<A, T> SerializeWith<T> for Base64Array<A>
180where
181 A: AlphabetTag,
182 T: AsRef<[u8]>,
183{
184 fn serialize_with<S: Serializer>(bytes: &T, serializer: S) -> Result<S::Ok, S::Error> {
185 Serialize::serialize(&A::VALUE.inner.encode(bytes), serializer)
186 }
187}
188
189impl<'de, A, const N: usize> DeserializeWith<'de, [u8; N]> for Base64Array<A>
190where
191 A: AlphabetTag,
192{
193 fn deserialize_with<D>(deserializer: D) -> Result<[u8; N], D::Error>
194 where
195 D: Deserializer<'de>,
196 {
197 deserializer.deserialize_str(Base64ArrayVisitor::<N>::new::<A>())
198 }
199}
200
201struct Base64ArrayVisitor<const N: usize> {
202 engine: &'static base64::engine::GeneralPurpose,
203}
204
205impl<const N: usize> Base64ArrayVisitor<N> {
206 const fn new<A>() -> Self
207 where
208 A: AlphabetTag,
209 {
210 Self {
211 engine: &A::VALUE.inner,
212 }
213 }
214}
215
216impl<const N: usize> Visitor<'_> for Base64ArrayVisitor<N> {
217 type Value = [u8; N];
218
219 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
220 write!(f, "a base64 string encoding {N} bytes")
221 }
222
223 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
224 where
225 E: serde::de::Error,
226 {
227 let mut out = [0u8; N];
228 let n = self.engine.decode_slice(v, &mut out).map_err(|e| match e {
229 base64::DecodeSliceError::DecodeError(_) => serde::de::Error::custom(e),
230 base64::DecodeSliceError::OutputSliceTooSmall => {
231 serde::de::Error::invalid_length(N + 1, &self)
232 }
233 })?;
234 Some(out)
235 .filter(|_| n == N)
236 .ok_or_else(|| serde::de::Error::invalid_length(n, &self))
237 }
238}
239
240pub trait AlphabetTag {
242 const VALUE: Alphabet;
244}
245
246#[derive(Debug)]
248pub struct Alphabet {
249 inner: base64::engine::GeneralPurpose,
250}
251
252impl Alphabet {
253 pub const fn new(chars: &[u8; 64], padding: PaddingStrategy) -> Alphabet {
257 Self {
258 inner: engine(chars, padding),
259 }
260 }
261}
262
263#[derive(Debug)]
265pub struct Standard;
266
267impl AlphabetTag for Standard {
268 const VALUE: Alphabet = Alphabet {
269 inner: base64::engine::general_purpose::STANDARD,
270 };
271}
272
273#[derive(Debug)]
275pub struct UrlSafe;
276
277impl AlphabetTag for UrlSafe {
278 const VALUE: Alphabet = Alphabet {
279 inner: base64::engine::general_purpose::URL_SAFE,
280 };
281}
282
283const fn engine(chars: &[u8; 64], padding: PaddingStrategy) -> base64::engine::GeneralPurpose {
284 let s = match core::str::from_utf8(chars) {
285 Ok(s) => s,
286 Err(_) => panic!("Invalid base64 alphabet"),
287 };
288 let alphabet = match base64::alphabet::Alphabet::new(s) {
289 Ok(a) => a,
290 Err(_) => panic!("Invalid base64 alphabet"),
291 };
292 base64::engine::GeneralPurpose::new(
293 &alphabet,
294 base64::engine::GeneralPurposeConfig::new()
295 .with_encode_padding(padding.write())
296 .with_decode_padding_mode(padding.padding_mode()),
297 )
298}
299
300#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
302#[non_exhaustive]
303pub enum PaddingStrategy {
304 Ignored,
306 Required,
308 None,
310}
311
312impl PaddingStrategy {
313 const fn padding_mode(self) -> base64::engine::DecodePaddingMode {
314 match self {
315 Self::Ignored => base64::engine::DecodePaddingMode::Indifferent,
316 Self::Required => base64::engine::DecodePaddingMode::RequireCanonical,
317 Self::None => base64::engine::DecodePaddingMode::RequireNone,
318 }
319 }
320
321 const fn write(self) -> bool {
322 match self {
323 Self::Required => true,
324 Self::Ignored | Self::None => false,
325 }
326 }
327}