serdapt_hex/
lib.rs

1// Copyright (c) 2025 Stephane Raux. Distributed under the 0BSD license.
2
3//! # Overview
4//! - [📦 crates.io](https://crates.io/crates/serdapt-hex)
5//! - [📖 Documentation](https://docs.rs/serdapt-hex)
6//! - [âš– 0BSD license](https://spdx.org/licenses/0BSD.html)
7//!
8//! Hex adapter for `#[serde(with = ...)]`. See [`serdapt`](https://docs.rs/serdapt) for more
9//! information on how to use such adapters.
10//!
11//! The documentation for [`Hex`] and [`HexArray`] provides examples.
12//!
13//! # Contribute
14//! All contributions shall be licensed under the [0BSD license](https://spdx.org/licenses/0BSD.html).
15
16#![deny(missing_docs)]
17#![no_std]
18
19extern crate alloc;
20
21use alloc::vec::Vec;
22use core::fmt::{self, Display};
23use serdapt::{DeserializeWith, SerializeWith};
24use serde::{de::Visitor, Deserializer, Serialize, Serializer};
25
26/// Adapter to serialize bytes as a hex string
27///
28/// `UPPERCASE` only affects serialization. Deserialization always accepts both lower and upper
29/// cases.
30///
31/// If the target type is an array, the [`HexArray`] adapter should perform better.
32///
33/// # Example
34/// ```
35/// # extern crate alloc;
36/// # use alloc::{vec, vec::Vec};
37/// use serde::{Deserialize, Serialize};
38/// use serde_json::json;
39///
40/// #[derive(Debug, Deserialize, PartialEq, Serialize)]
41/// struct Foo(#[serde(with = "serdapt_hex::LowHex")] Vec<u8>);
42///
43/// let x = Foo(vec![9, 1, 74]);
44/// let v = serde_json::to_value(&x).unwrap();
45/// assert_eq!(v, json!("09014a"));
46/// let x2 = serde_json::from_value::<Foo>(v).unwrap();
47/// assert_eq!(x, x2);
48/// ```
49#[non_exhaustive]
50pub struct Hex<const UPPERCASE: bool = false> {}
51
52/// Adapter to serialize bytes as a lowercase hex string
53pub type LowHex = Hex<false>;
54
55/// Adapter to serialize bytes as an uppercase hex string
56pub type UpHex = Hex<true>;
57
58impl<const UPPERCASE: bool> Hex<UPPERCASE> {
59    /// Serializes value with adapter
60    pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
61    where
62        T: ?Sized,
63        S: Serializer,
64        Self: SerializeWith<T>,
65    {
66        Self::serialize_with(value, serializer)
67    }
68
69    /// Deserializes value with adapter
70    pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
71    where
72        D: Deserializer<'de>,
73        Self: DeserializeWith<'de, T>,
74    {
75        Self::deserialize_with(deserializer)
76    }
77}
78
79impl<T> SerializeWith<T> for Hex<false>
80where
81    T: AsRef<[u8]>,
82{
83    fn serialize_with<S: Serializer>(value: &T, serializer: S) -> Result<S::Ok, S::Error> {
84        Serialize::serialize(&hex::encode(value), serializer)
85    }
86}
87
88impl<T> SerializeWith<T> for Hex<true>
89where
90    T: AsRef<[u8]>,
91{
92    fn serialize_with<S: Serializer>(value: &T, serializer: S) -> Result<S::Ok, S::Error> {
93        Serialize::serialize(&hex::encode_upper(value), serializer)
94    }
95}
96
97impl<'de, const U: bool, T> DeserializeWith<'de, T> for Hex<U>
98where
99    T: TryFrom<Vec<u8>>,
100    T::Error: Display,
101{
102    fn deserialize_with<D>(deserializer: D) -> Result<T, D::Error>
103    where
104        D: Deserializer<'de>,
105    {
106        let bytes = deserializer.deserialize_str(HexVisitor)?;
107        bytes.try_into().map_err(serde::de::Error::custom)
108    }
109}
110
111struct HexVisitor;
112
113impl Visitor<'_> for HexVisitor {
114    type Value = Vec<u8>;
115
116    fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
117        f.write_str("a hex string")
118    }
119
120    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
121    where
122        E: serde::de::Error,
123    {
124        hex::decode(v).map_err(serde::de::Error::custom)
125    }
126}
127
128/// Adapter to serialize a byte array as a hex string
129///
130/// `UPPERCASE` only affects serialization. Deserialization always accepts both lower and upper
131/// cases.
132///
133/// # Example
134/// ```
135/// # extern crate alloc;
136/// # use alloc::vec;
137/// use serde::{Deserialize, Serialize};
138/// use serde_json::json;
139///
140/// #[derive(Debug, Deserialize, PartialEq, Serialize)]
141/// struct Foo(#[serde(with = "serdapt_hex::UpHexArray")] [u8; 3]);
142///
143/// let x = Foo([9, 1, 74]);
144/// let v = serde_json::to_value(&x).unwrap();
145/// assert_eq!(v, json!("09014A"));
146/// let x2 = serde_json::from_value::<Foo>(v).unwrap();
147/// assert_eq!(x, x2);
148/// ```
149#[non_exhaustive]
150pub struct HexArray<const UPPERCASE: bool = false> {}
151
152/// Adapter to serialize a byte array as a lowercase hex string
153pub type LowHexArray = HexArray<false>;
154
155/// Adapter to serialize a byte array as an uppercase hex string
156pub type UpHexArray = HexArray<true>;
157
158impl<const U: bool> HexArray<U> {
159    /// Serializes value with adapter
160    pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
161    where
162        T: ?Sized,
163        S: Serializer,
164        Self: SerializeWith<T>,
165    {
166        Self::serialize_with(value, serializer)
167    }
168
169    /// Deserializes value with adapter
170    pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
171    where
172        D: Deserializer<'de>,
173        Self: DeserializeWith<'de, T>,
174    {
175        Self::deserialize_with(deserializer)
176    }
177}
178
179impl<T> SerializeWith<T> for HexArray<false>
180where
181    T: AsRef<[u8]>,
182{
183    fn serialize_with<S: Serializer>(value: &T, serializer: S) -> Result<S::Ok, S::Error> {
184        Serialize::serialize(&hex::encode(value), serializer)
185    }
186}
187
188impl<T> SerializeWith<T> for HexArray<true>
189where
190    T: AsRef<[u8]>,
191{
192    fn serialize_with<S: Serializer>(value: &T, serializer: S) -> Result<S::Ok, S::Error> {
193        Serialize::serialize(&hex::encode_upper(value), serializer)
194    }
195}
196
197impl<'de, const U: bool, const N: usize> DeserializeWith<'de, [u8; N]> for HexArray<U> {
198    fn deserialize_with<D>(deserializer: D) -> Result<[u8; N], D::Error>
199    where
200        D: Deserializer<'de>,
201    {
202        deserializer.deserialize_str(HexArrayVisitor::<N>)
203    }
204}
205
206struct HexArrayVisitor<const N: usize>;
207
208impl<const N: usize> Visitor<'_> for HexArrayVisitor<N> {
209    type Value = [u8; N];
210
211    fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
212        write!(f, "a hex string encoding {N} bytes")
213    }
214
215    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
216    where
217        E: serde::de::Error,
218    {
219        let mut out = [0u8; N];
220        hex::decode_to_slice(v, &mut out).map_err(|e| match e {
221            hex::FromHexError::InvalidStringLength => E::invalid_length(v.len() / 2, &self),
222            _ => E::custom(e),
223        })?;
224        Ok(out)
225    }
226}