serde_bytes_repr/lib.rs
1//! # Serde adapter for (de)serializing bytes to and from specific formats
2//!
3//! Currently these representations is supported:
4//!
5//! - Base64
6//! - Hexidecimal
7//!
8//! Human readable formats tend not to include a universally agreed way to represent arbitrary binary
9//! data, which means those serde libraries can end up using a representation for serde's "bytes" type
10//! which isn't ideal for all uses. This library gives you the option to choose a different
11//! representation than the default for libraries like `serde_json`, `toml` and `serde_yaml`.
12//!
13//! ## How to make sure that your datatype is interpreted as bytes?
14//!
15//! Without specialization, Rust forces Serde to treat &[u8] just like any other
16//! slice and Vec<u8> just like any other vector. To enable specialized handling
17//! of `&[u8]` can use the [serde_bytes](https://docs.rs/serde_bytes/0.11.5/serde_bytes/index.html) crate.
18//!
19//! You'll se this in use in the examples below:
20//!
21//! ## Serialization
22//!
23//! ```rust
24//! use serde::{Deserialize, Serialize};
25//! use serde_bytes_repr::ByteFmtSerializer;
26//! # fn main() {
27//! #[derive(Serialize, Deserialize)]
28//! struct Demo {
29//! #[serde(with = "serde_bytes")]
30//! bytes: Vec<u8>,
31//! }
32//! let bytes = b"testing".to_vec();
33//! let demo = Demo { bytes };
34//!
35//! let mut out = vec![];
36//! let mut ser = serde_json::Serializer::new(&mut out);
37//! let base64_config = base64::engine::GeneralPurposeConfig::new();
38//! let ser = ByteFmtSerializer::base64(&mut ser, base64::alphabet::URL_SAFE, base64_config);
39//! demo.serialize(ser).unwrap();
40//!
41//! let serialized = String::from_utf8(out).unwrap();
42//! assert_eq!(r#"{"bytes":"dGVzdGluZw=="}"#, serialized.as_str());
43//! # }
44//! ```
45//!
46//! ## Deserialization
47//!
48//! ```rust
49//! use serde::{Deserialize, Serialize};
50//! use serde_bytes_repr::{ByteFmtDeserializer, ByteFmtSerializer};
51//! # fn main() {
52//! #[derive(Serialize, Deserialize)]
53//! struct Demo {
54//! #[serde(with = "serde_bytes")]
55//! bytes: Vec<u8>,
56//! }
57//!
58//! let json = br#"{"bytes":"dGVzdGluZw=="}"#;
59//! let mut json_de = serde_json::Deserializer::from_slice(json);
60//! let base64_config = base64::engine::GeneralPurposeConfig::new();
61//! let bytefmt_json_de = ByteFmtDeserializer::new_base64(&mut json_de, base64::alphabet::URL_SAFE, base64_config);
62//! let demo: Demo = Demo::deserialize(bytefmt_json_de).unwrap();
63//!
64//! let deserialized = String::from_utf8(demo.bytes).unwrap();
65//! assert_eq!("testing", deserialized.as_str());
66//! # }
67//! ```
68
69use base64::{alphabet::Alphabet, engine::GeneralPurposeConfig, Engine};
70
71mod deserializer;
72mod serializer;
73
74#[derive(Clone)]
75enum ByteFormat {
76 Base64(Alphabet, GeneralPurposeConfig),
77 Hex,
78}
79
80/// Serializer-adapter which encodes bytes to using the specified encoding. The format is
81/// serialized to the data formats string representation.
82pub struct ByteFmtSerializer<S> {
83 inner: S,
84 encode_kind: ByteFormat,
85}
86impl<S> ByteFmtSerializer<S> {
87 /// Crates an adapter which serializes to and from a Base64 representation.
88 /// Provide a configuration from the `base64` crate specifying the specifics
89 /// on how you want the bytes encoded.
90 pub fn base64(ser: S, alphabet: Alphabet, config: GeneralPurposeConfig) -> Self {
91 Self {
92 inner: ser,
93 encode_kind: ByteFormat::Base64(alphabet, config),
94 }
95 }
96
97 /// Creates an adapter which serializes to a HEX representation.
98 pub fn hex(ser: S) -> Self {
99 Self {
100 inner: ser,
101 encode_kind: ByteFormat::Hex,
102 }
103 }
104
105 fn encode(&self, v: &[u8]) -> String {
106 match self.encode_kind {
107 ByteFormat::Base64(ref alphabet, config) => {
108 base64::engine::GeneralPurpose::new(alphabet, config).encode(v)
109 }
110 ByteFormat::Hex => hex::encode(v),
111 }
112 }
113}
114
115/// Deserializer-adapter which decodes bytes from a specified format.
116pub struct ByteFmtDeserializer<D> {
117 pub inner: D,
118 fmt: ByteFormat,
119}
120
121impl<D> ByteFmtDeserializer<D> {
122 /// Crates an adapter which deserializes from a Base64 representation. Provide a
123 /// configuration from the `base64` crate specifying the specifics on how you want the bytes
124 /// encoded.
125 ///
126 pub fn new_base64(deserializer: D, alphabet: Alphabet, config: GeneralPurposeConfig) -> Self {
127 ByteFmtDeserializer {
128 inner: deserializer,
129 fmt: ByteFormat::Base64(alphabet, config),
130 }
131 }
132
133 /// Creates an adapter which deserializes from a HEX representation.
134 pub fn new_hex(deserializer: D) -> Self {
135 ByteFmtDeserializer {
136 inner: deserializer,
137 fmt: ByteFormat::Hex,
138 }
139 }
140}