Skip to main content

deser_core/
bytes_format.rs

1use alloc::string::String;
2use alloc::vec::Vec;
3use core::fmt;
4
5use crate::State;
6use crate::adapters::bytes::decode_base64;
7use crate::adapters::{Base64, BytesEncoding};
8use crate::error::Error;
9
10/// How bytes are represented in formats without native bytes.
11///
12/// This is used in three places:
13///
14/// * The serializers of formats without native bytes (JSON and TOML) can be
15///   configured with a format.  It's used for all bytes that do not request
16///   a format.  The default is [`BytesFormat::BASE64`].
17/// * Bytes can carry a format as fallback (see
18///   [`Bytes::fallback`](crate::Bytes::fallback)) which takes precedence
19///   over the configuration of the serializer.  The
20///   [`BytesFallback`](crate::adapters::BytesFallback) adapter does this.
21/// * The types that expect bytes decode strings with the format placed into
22///   the [`State`] (see [`set`](Self::set)).  The deserializers of formats
23///   without native bytes can be configured to do this, otherwise lenient
24///   base64 is used.  Strings are decoded as base64 for
25///   [`BytesFormat::SEQ`].
26///
27/// ```
28/// use deser::adapters::Base64UrlNoPad;
29/// use deser::BytesFormat;
30///
31/// const URL_SAFE: BytesFormat = BytesFormat::encoded::<Base64UrlNoPad>();
32/// assert_eq!(URL_SAFE.encode(b"\xfb\xff").as_deref(), Some("-_8"));
33/// // decoding is lenient
34/// assert_eq!(URL_SAFE.decode("+/8=").unwrap(), b"\xfb\xff");
35/// assert_eq!(BytesFormat::SEQ.encode(b"\x01\xff"), None);
36/// ```
37///
38/// Two formats are equal if they are both sequences or encodings with the
39/// same [name](crate::adapters::BytesEncoding::NAME).
40#[derive(Clone, Copy)]
41pub struct BytesFormat(Repr);
42
43#[derive(Clone, Copy)]
44enum Repr {
45    Encoded {
46        name: &'static str,
47        encode: fn(&[u8], &mut String),
48        decode: fn(&str) -> Result<Vec<u8>, Error>,
49    },
50    Seq,
51}
52
53impl BytesFormat {
54    /// Bytes are strings in base64 with the standard alphabet and padding.
55    ///
56    /// This is the default.
57    pub const BASE64: BytesFormat = BytesFormat::encoded::<Base64>();
58
59    /// Bytes are sequences of integers.
60    ///
61    /// This is how `serde_json` represents bytes.
62    pub const SEQ: BytesFormat = BytesFormat(Repr::Seq);
63
64    /// Bytes are strings in the given encoding.
65    pub const fn encoded<E: BytesEncoding>() -> BytesFormat {
66        BytesFormat(Repr::Encoded {
67            name: E::NAME,
68            encode: E::encode,
69            decode: E::decode,
70        })
71    }
72
73    /// Returns the format the types that expect bytes decode strings with.
74    ///
75    /// This is [`BytesFormat::BASE64`] unless the deserializer of the format
76    /// [`set`](Self::set) a different one.
77    #[inline]
78    pub fn of(state: &State) -> BytesFormat {
79        state.get::<BytesFormat>().copied().unwrap_or_default()
80    }
81
82    /// Sets the format the types that expect bytes decode strings with.
83    #[inline]
84    pub fn set(self, state: &mut State) {
85        *state.get_mut::<BytesFormat>() = self;
86    }
87
88    /// Returns the name of the format.
89    ///
90    /// This is the [name](crate::adapters::BytesEncoding::NAME) of the encoding or `seq`.
91    pub fn name(&self) -> &'static str {
92        match self.0 {
93            Repr::Encoded { name, .. } => name,
94            Repr::Seq => "seq",
95        }
96    }
97
98    /// Returns `true` if bytes are sequences of integers.
99    pub fn is_seq(&self) -> bool {
100        matches!(self.0, Repr::Seq)
101    }
102
103    /// Encodes bytes as string.
104    ///
105    /// Returns `None` if bytes are sequences of integers.
106    pub fn encode(&self, bytes: &[u8]) -> Option<String> {
107        match self.0 {
108            Repr::Encoded { encode, .. } => {
109                let mut rv = String::new();
110                encode(bytes, &mut rv);
111                Some(rv)
112            }
113            Repr::Seq => None,
114        }
115    }
116
117    /// Decodes bytes from a string.
118    ///
119    /// For sequences of integers the string is decoded as base64.
120    pub fn decode(&self, s: &str) -> Result<Vec<u8>, Error> {
121        match self.0 {
122            Repr::Encoded { decode, .. } => decode(s),
123            Repr::Seq => decode_base64(s),
124        }
125    }
126}
127
128impl Default for BytesFormat {
129    fn default() -> BytesFormat {
130        BytesFormat::BASE64
131    }
132}
133
134impl fmt::Debug for BytesFormat {
135    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136        f.debug_tuple("BytesFormat").field(&self.name()).finish()
137    }
138}
139
140impl PartialEq for BytesFormat {
141    fn eq(&self, other: &Self) -> bool {
142        match (self.0, other.0) {
143            (Repr::Encoded { name: a, .. }, Repr::Encoded { name: b, .. }) => a == b,
144            (Repr::Seq, Repr::Seq) => true,
145            _ => false,
146        }
147    }
148}
149
150impl Eq for BytesFormat {}
151
152#[cfg(test)]
153mod tests {
154    use super::*;
155    use crate::adapters::Base64Url;
156
157    #[test]
158    fn test_format() {
159        assert_eq!(BytesFormat::default(), BytesFormat::BASE64);
160        assert_eq!(BytesFormat::encoded::<Base64>(), BytesFormat::BASE64);
161        assert_ne!(BytesFormat::encoded::<Base64Url>(), BytesFormat::BASE64);
162        assert_ne!(BytesFormat::SEQ, BytesFormat::BASE64);
163        assert_eq!(format!("{:?}", BytesFormat::SEQ), "BytesFormat(\"seq\")");
164        assert_eq!(BytesFormat::SEQ.decode("AQ==").unwrap(), b"\x01");
165    }
166}