biscuit/serde_custom/
byte_sequence.rs

1//! Serialize a sequence of bytes as base64 URL encoding vice-versa for deserialization
2use std::fmt;
3
4use data_encoding::BASE64URL_NOPAD;
5use serde::de;
6use serde::{Deserializer, Serializer};
7
8/// Serialize a byte sequence into Base64 URL encoded string
9pub fn serialize<S>(value: &[u8], serializer: S) -> Result<S::Ok, S::Error>
10where
11    S: Serializer,
12{
13    let base64 = BASE64URL_NOPAD.encode(value);
14    serializer.serialize_str(&base64)
15}
16
17/// Deserialize a byte sequence from Base64 URL encoded string
18pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
19where
20    D: Deserializer<'de>,
21{
22    struct BytesVisitor;
23
24    impl<'de> de::Visitor<'de> for BytesVisitor {
25        type Value = Vec<u8>;
26
27        fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
28            formatter.write_str("a URL safe base64 encoding of a byte sequence")
29        }
30
31        fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
32        where
33            E: de::Error,
34        {
35            let bytes = BASE64URL_NOPAD
36                .decode(value.as_bytes())
37                .map_err(E::custom)?;
38            Ok(bytes)
39        }
40    }
41
42    deserializer.deserialize_str(BytesVisitor)
43}
44
45pub struct Wrapper<'a>(&'a [u8]);
46
47pub fn wrap(data: &[u8]) -> Wrapper {
48    Wrapper(data)
49}
50
51impl<'a> serde::Serialize for Wrapper<'a> {
52    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
53    where
54        S: Serializer,
55    {
56        serialize(self.0, serializer)
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use serde::{Deserialize, Serialize};
63    use serde_test::{assert_tokens, Token};
64
65    #[derive(Serialize, Deserialize, Eq, PartialEq, Debug)]
66    struct TestStruct {
67        #[serde(with = "super")]
68        bytes: Vec<u8>,
69    }
70
71    #[test]
72    fn serialization_round_trip() {
73        let test_value = TestStruct {
74            bytes: "hello world".to_string().into_bytes(),
75        };
76
77        assert_tokens(
78            &test_value,
79            &[
80                Token::Struct {
81                    name: "TestStruct",
82                    len: 1,
83                },
84                Token::Str("bytes"),
85                Token::Str("aGVsbG8gd29ybGQ"),
86                Token::StructEnd,
87            ],
88        );
89    }
90}