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

use base64::{alphabet::Alphabet, engine::GeneralPurposeConfig, Engine};

mod deserializer;
mod serializer;

#[derive(Clone)]
enum ByteFormat {
    Base64(Alphabet, GeneralPurposeConfig),
    Hex,
}

/// Serializer-adapter which encodes bytes to using the specified encoding. The format is
/// serialized to the data formats string representation.
pub struct ByteFmtSerializer<S> {
    inner: S,
    encode_kind: ByteFormat,
}
impl<S> ByteFmtSerializer<S> {
    /// Crates an adapter which serializes to and from a Base64 representation.
    /// Provide a configuration from the `base64` crate specifying the specifics
    /// on how you want the bytes encoded.
    pub fn base64(ser: S, alphabet: Alphabet, config: GeneralPurposeConfig) -> Self {
        Self {
            inner: ser,
            encode_kind: ByteFormat::Base64(alphabet, config),
        }
    }

    /// Creates an adapter which serializes to a HEX representation.
    pub fn hex(ser: S) -> Self {
        Self {
            inner: ser,
            encode_kind: ByteFormat::Hex,
        }
    }

    fn encode(&self, v: &[u8]) -> String {
        match self.encode_kind {
            ByteFormat::Base64(ref alphabet, config) => {
                base64::engine::GeneralPurpose::new(alphabet, config).encode(v)
            }
            ByteFormat::Hex => hex::encode(v),
        }
    }
}

/// Deserializer-adapter which decodes bytes from a specified format.
pub struct ByteFmtDeserializer<D> {
    pub inner: D,
    fmt: ByteFormat,
}

impl<D> ByteFmtDeserializer<D> {
    /// Crates an adapter which deserializes from a Base64 representation. Provide a
    /// configuration from the `base64` crate specifying the specifics on how you want the bytes
    /// encoded.
    ///
    pub fn new_base64(deserializer: D, alphabet: Alphabet, config: GeneralPurposeConfig) -> Self {
        ByteFmtDeserializer {
            inner: deserializer,
            fmt: ByteFormat::Base64(alphabet, config),
        }
    }

    /// Creates an adapter which deserializes from a HEX representation.
    pub fn new_hex(deserializer: D) -> Self {
        ByteFmtDeserializer {
            inner: deserializer,
            fmt: ByteFormat::Hex,
        }
    }
}