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}