stremio_serde_hex/
lib.rs

1//! The `serde-hex` crate contains various utilities for Serialization/Deserialization
2//! of hexadecimal values using [`serde`](https://crates.io/crates/serde).
3//!
4//! The core utility of this crate is the `SerHex` trait. Once implemented, `SerHex`
5//! allows for easy configuration of hexadecimal serialization/deserialization with
6//! `serde-derive`:
7//!
8//! ```rust
9//! use stremio_serde_hex::{SerHex, StrictPfx};
10//! use serde::{Serialize, Deserialize};
11//!
12//! #[derive(Debug, Serialize, Deserialize)]
13//! struct Foo {
14//!    #[serde(with = "SerHex::<StrictPfx>")]
15//!    bar: [u8; 32]
16//! }
17//!
18//! # fn main() {}
19//! ```
20//!
21//! The above example will cause serde to serialize `Bar` into a hexadecimal string
22//! with strict sizing (padded with leading zeroes), and prefixing (leading `0x`).
23//! The possible configurations allow for any combination of strict/compact
24//! representations, prefixing, and capitalizing (e.g.; `Compact`,
25//! `StrictCapPfx`, etc...).
26//!
27//! This crate provides implementations of `SerHex` for all unsigned integer types,
28//! as well as generic impls for arrays of types which implement `SerHex`.  The generic
29//! impls apply only to strict variants of the trait, and only for arrays of length 1
30//! through 64 (no impl is provided for arrays of length 0 since there isn't really
31//! a reasonable way to represent a zero-sized value in hex).
32//!
33//!
34//!
35#![warn(missing_docs)]
36
37#[macro_use]
38pub mod macros;
39pub mod config;
40pub mod types;
41pub mod utils;
42
43#[doc(inline)]
44pub use config::*;
45#[doc(inline)]
46pub use types::{Error, ParseHexError};
47
48use core::{iter::FromIterator, marker::PhantomData};
49use std::{error, fmt, io};
50
51use serde::{de::Visitor, Deserializer, Serializer};
52use smallvec::SmallVec;
53
54/// Trait specifying custom serialization and deserialization logic from a
55/// hexadecimal string to some arbitrary type.  This trait can be used to apply
56/// custom parsing when using serde's `#[derive(Serialize,Deserialize)]`
57/// flag.  Just add `#[serde(with = "SerHex")]` above any fields which implement
58/// this trait.  Simplistic default implimentations for the the `serialize` and
59/// `deserialize` methods are provided based on `into_hex_raw` and `from_hex_raw` respectively.
60pub trait SerHex<C>: Sized
61where
62    C: HexConf,
63{
64    /// Error type of the implementation.
65    ///
66    /// Unless you have a compelling reason to do so, it is best to use the error
67    /// type exposed by `serde-hex`, since this is the error used for most provided
68    /// implementations (the generic array impls will work with any error that
69    /// implements [`From`](https://doc.rust-lang.org/std/convert/trait.From.html)
70    /// for the `serde-hex` error type).
71    type Error: error::Error;
72
73    /// Attept to convert `self` to hexadecimal, writing the resultant bytes to some buffer.
74    fn into_hex_raw<D>(&self, dst: D) -> Result<(), Self::Error>
75    where
76        D: io::Write;
77
78    /// Attempt to parse some buffer of hexadecimal bytes into an instance of `Self`.
79    fn from_hex_raw<S>(src: S) -> Result<Self, Self::Error>
80    where
81        S: AsRef<[u8]>;
82
83    /// Attempt to convert `self` into a hexadecimal string representation.
84    fn into_hex(&self) -> Result<String, Self::Error> {
85        let mut dst: Vec<u8> = Vec::with_capacity(32);
86        self.into_hex_raw(&mut dst)?;
87        Ok(String::from_utf8(dst).expect("invalid UTF-8 bytes in parsing"))
88    }
89
90    /// Attempt to convert a slice of hexadecimal bytes into an instance of `Self`.
91    fn from_hex<S>(src: S) -> Result<Self, Self::Error>
92    where
93        S: AsRef<[u8]>,
94    {
95        Self::from_hex_raw(src)
96    }
97
98    /// Attempt to serialize `self` into a hexadecimal string representation.
99    ///
100    /// *NOTE*: The default implementation attempts to avoid heap-allocation with a
101    /// [`SmallVec`](https://docs.rs/smallvec/) of size `[u8;64]`. This default will
102    /// prevent heap-alloc for non-prefixed serializations of `[u8;32]` or smaller.
103    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
104    where
105        S: Serializer,
106    {
107        use serde::ser::Error;
108        let mut dst = SmallVec::<[u8; 64]>::new();
109        self.into_hex_raw(&mut dst).map_err(S::Error::custom)?;
110        // if `dst` is not valid UTF-8 bytes, the underlying implementation
111        // is very broken, and you should be ashamed of yourelf.
112        debug_assert!(::std::str::from_utf8(dst.as_ref()).is_ok());
113        let s = unsafe { ::std::str::from_utf8_unchecked(dst.as_ref()) };
114        serializer.serialize_str(s)
115    }
116
117    /// Attempt to deserialize a hexadecimal string into an instance of `Self`.
118    fn deserialize<'de, D>(deserializer: D) -> Result<Self, D::Error>
119    where
120        D: Deserializer<'de>,
121    {
122        let rslt = deserializer.deserialize_any(HexBytesVisitor::default())?;
123        Ok(rslt)
124    }
125}
126
127struct HexBytesVisitor<S, C> {
128    _phantom: PhantomData<(S, C)>,
129}
130
131impl<S, C> Default for HexBytesVisitor<S, C> {
132    fn default() -> Self {
133        Self {
134            _phantom: PhantomData,
135        }
136    }
137}
138
139impl<'de, S, C> Visitor<'de> for HexBytesVisitor<S, C>
140where
141    S: SerHex<C>,
142    C: HexConf,
143{
144    type Value = S;
145
146    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
147        formatter.write_str("a hex string")
148    }
149
150    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
151    where
152        E: serde::de::Error,
153    {
154        S::from_hex_raw(v).map_err(E::custom)
155    }
156
157    fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
158    where
159        E: serde::de::Error,
160    {
161        S::from_hex_raw(v).map_err(E::custom)
162    }
163
164    fn visit_borrowed_bytes<E>(self, v: &'de [u8]) -> Result<Self::Value, E>
165    where
166        E: serde::de::Error,
167    {
168        S::from_hex_raw(v).map_err(E::custom)
169    }
170}
171
172/// Variant of `SerHex` for serializing/deserializing `Option` types.
173///
174/// Any type `T` which implements `SerHex<C>` implements `SerHexOpt<C>`
175/// automatically.
176///
177/// ```rust
178/// # use serde::{Serialize, Deserialize};
179/// # use stremio_serde_hex::{SerHexOpt,CompactPfx};
180/// #
181/// #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
182/// struct MaybeNum {
183///     #[serde(with = "SerHexOpt::<CompactPfx>")]
184///     num: Option<u64>
185/// }
186///
187/// # fn main() {
188/// let s: MaybeNum = serde_json::from_str(r#"{"num":"0xff"}"#).unwrap();
189/// assert_eq!(s,MaybeNum { num: Some(255) });
190///
191/// let n: MaybeNum = serde_json::from_str(r#"{"num":null}"#).unwrap();
192/// assert_eq!(n,MaybeNum { num: None });
193/// # }
194/// ```
195///
196pub trait SerHexOpt<C>: Sized + SerHex<C>
197where
198    C: HexConf,
199{
200    /// Same as `SerHex::serialize`, except for `Option<Self>` instead of `Self`.
201    fn serialize<S>(option: &Option<Self>, serializer: S) -> Result<S::Ok, S::Error>
202    where
203        S: Serializer,
204    {
205        use serde::ser::Error;
206        if let Some(ref src) = *option {
207            let mut dst = SmallVec::<[u8; 64]>::new();
208            Self::into_hex_raw(src, &mut dst).map_err(S::Error::custom)?;
209            // if `dst` is not valid UTF-8 bytes, the underlying implementation
210            // is very broken, and you should be ashamed of yourelf.
211            debug_assert!(::std::str::from_utf8(dst.as_ref()).is_ok());
212            let s = unsafe { ::std::str::from_utf8_unchecked(dst.as_ref()) };
213            //serializer.serialize_str(s)
214            serializer.serialize_some(s)
215        } else {
216            serializer.serialize_none()
217        }
218    }
219
220    /// Same as `SerHex::deserialize`, except for `Option<Self>` instead of `Self`.
221    fn deserialize<'de, D>(deserializer: D) -> Result<Option<Self>, D::Error>
222    where
223        D: Deserializer<'de>,
224    {
225        let option = deserializer.deserialize_any(OptHexBytesVisitor::default())?;
226
227        Ok(option)
228    }
229}
230
231impl<S, C> SerHexOpt<C> for S
232where
233    S: Sized + SerHex<C>,
234    C: HexConf,
235{
236}
237
238struct OptHexBytesVisitor<S, C> {
239    _phantom: PhantomData<(S, C)>,
240}
241
242impl<T, C> Default for OptHexBytesVisitor<T, C> {
243    fn default() -> Self {
244        Self {
245            _phantom: PhantomData,
246        }
247    }
248}
249
250impl<'de, S, C> Visitor<'de> for OptHexBytesVisitor<S, C>
251where
252    S: SerHexOpt<C>,
253    C: HexConf,
254{
255    type Value = Option<S>;
256
257    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
258        formatter.write_str("a hex string")
259    }
260
261    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
262    where
263        E: serde::de::Error,
264    {
265        let s = S::from_hex_raw(v).map_err(E::custom)?;
266
267        Ok(Some(s))
268    }
269
270    fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
271    where
272        E: serde::de::Error,
273    {
274        let s = S::from_hex_raw(v).map_err(E::custom)?;
275
276        Ok(Some(s))
277    }
278
279    fn visit_borrowed_bytes<E>(self, v: &'de [u8]) -> Result<Self::Value, E>
280    where
281        E: serde::de::Error,
282    {
283        let s = S::from_hex_raw(v).map_err(E::custom)?;
284
285        Ok(Some(s))
286    }
287
288    fn visit_none<E>(self) -> Result<Self::Value, E>
289    where
290        E: serde::de::Error,
291    {
292        Ok(None)
293    }
294
295    fn visit_unit<E>(self) -> Result<Self::Value, E>
296    where
297        E: serde::de::Error,
298    {
299        Ok(None)
300    }
301
302    fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, <D as Deserializer<'de>>::Error>
303    where
304        D: Deserializer<'de>,
305    {
306        let result = deserializer.deserialize_bytes(self)?;
307
308        Ok(result)
309    }
310}
311
312/// Variant of `SerHex` for serializing/deserializing sequence types as
313/// contiguous hexadecimal strings.
314///
315/// *NOTE*: `Compact` configurations are not compatible with this trait.
316/// The size of each element must be consistent in order to avoid ambiguous
317/// encoding.
318///
319/// ```rust
320/// # use serde::{Serialize, Deserialize};
321/// # use stremio_serde_hex::{SerHexSeq,StrictPfx};
322/// #
323/// #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
324/// struct Bytes(#[serde(with = "SerHexSeq::<StrictPfx>")] Vec<u8>);
325///
326/// # fn main() {
327/// let bytes: Bytes = serde_json::from_str(r#""0xdeadbeef""#).unwrap();
328/// assert_eq!(bytes,Bytes(vec![0xde,0xad,0xbe,0xef]));
329/// # }
330/// ```
331///
332pub trait SerHexSeq<C>: Sized + SerHex<Strict> + SerHex<StrictCap>
333where
334    C: HexConf,
335{
336    /// expected size (in bytes) of a single element.  used to partition
337    /// the hexadecimal string into individual elements.
338    fn size() -> usize;
339
340    /// Same as `SerHex::serialize`, but for sequences of `Self`.
341    fn serialize<'a, S, T>(sequence: T, serializer: S) -> Result<S::Ok, S::Error>
342    where
343        S: Serializer,
344        T: IntoIterator<Item = &'a Self>,
345        Self: 'a,
346    {
347        use serde::ser::Error;
348        let mut dst = SmallVec::<[u8; 128]>::new();
349        if <C as HexConf>::withpfx() {
350            dst.extend_from_slice(b"0x");
351        }
352        if <C as HexConf>::withcap() {
353            for elem in sequence.into_iter() {
354                <Self as SerHex<StrictCap>>::into_hex_raw(elem, &mut dst)
355                    .map_err(S::Error::custom)?;
356            }
357        } else {
358            for elem in sequence.into_iter() {
359                <Self as SerHex<Strict>>::into_hex_raw(elem, &mut dst).map_err(S::Error::custom)?;
360            }
361        }
362        let s = unsafe { ::std::str::from_utf8_unchecked(dst.as_ref()) };
363        serializer.serialize_str(s)
364    }
365
366    /// Same as `SerHex::deserialize`, but for sequences of `Self`.
367    fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
368    where
369        D: Deserializer<'de>,
370        T: FromIterator<Self>,
371    {
372        deserializer.deserialize_bytes(SeqHexBytesVisitor::<Self, C, T>::default())
373    }
374}
375
376struct SeqHexBytesVisitor<S, C, T> {
377    _phantom: PhantomData<(S, C, T)>,
378}
379
380impl<S, C, T> Default for SeqHexBytesVisitor<S, C, T> {
381    fn default() -> Self {
382        Self {
383            _phantom: PhantomData,
384        }
385    }
386}
387
388impl<'de, S, C, T> Visitor<'de> for SeqHexBytesVisitor<S, C, T>
389where
390    S: SerHexSeq<C>,
391    C: HexConf,
392    T: FromIterator<S>,
393{
394    type Value = T;
395
396    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
397        formatter.write_str("a hex string")
398    }
399
400    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
401    where
402        E: serde::de::Error,
403    {
404        seq_from_bytes(v.as_bytes(), S::size())
405    }
406
407    fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
408    where
409        E: serde::de::Error,
410    {
411        seq_from_bytes(v.as_bytes(), S::size())
412    }
413
414    fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
415    where
416        E: serde::de::Error,
417    {
418        seq_from_bytes(v, S::size())
419    }
420
421    fn visit_borrowed_bytes<E>(self, v: &'de [u8]) -> Result<Self::Value, E>
422    where
423        E: serde::de::Error,
424    {
425        seq_from_bytes(v, S::size())
426    }
427}
428
429fn seq_from_bytes<S, E, T>(raw: &[u8], size_hint: usize) -> Result<T, E>
430where
431    S: SerHex<Strict>,
432    E: serde::de::Error,
433    T: FromIterator<S>,
434{
435    let src = if raw.starts_with(b"0x") {
436        &raw[2..]
437    } else {
438        &raw
439    };
440
441    let hexsize = size_hint * 2;
442    if src.len() % hexsize == 0 && hexsize != 0 && !src.is_empty() {
443        // if src.len() % hexsize == 0 {
444        let mut buff = Vec::with_capacity(src.len() / hexsize);
445        // if chunk size is 0 then chunks() will panic!
446        for chunk in src.chunks(hexsize) {
447            let elem = S::from_hex_raw(chunk).map_err(E::custom)?;
448            buff.push(elem);
449        }
450        Ok(buff.into_iter().collect())
451    } else {
452        Err(E::custom("bad hexadecimal sequence size"))
453    }
454}
455
456impl_serhex_uint!(u8, 1);
457impl_serhex_uint!(u16, 2);
458impl_serhex_uint!(u32, 4);
459impl_serhex_uint!(u64, 8);
460
461// implement strict variants of `SerHex` for arrays of `T` with
462// lengths of 1 through 64 (where `T` implements the strict variants
463// of `SerHex` as well).
464impl_serhex_strict_array!(
465    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,
466    27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
467    51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64
468);