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