Skip to main content

byte_wrapper/
hex_vec.rs

1// Copyright (c) The byte-wrapper Contributors
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! The [`HexVec`] newtype wrapper.
5
6use crate::{
7    HexSlice, ParseHexError, hex_array::decode_hex_into, hex_slice::HexDisplay,
8};
9use alloc::{borrow::ToOwned, vec, vec::Vec};
10use core::{borrow::Borrow, fmt, str::FromStr};
11
12/// A byte vector that displays and parses as hex.
13///
14/// `HexVec` wraps `Vec<u8>`, providing [`Display`](fmt::Display),
15/// [`FromStr`], [`LowerHex`](fmt::LowerHex), and
16/// [`UpperHex`](fmt::UpperHex) implementations that use hexadecimal
17/// encoding.
18///
19/// With the **`serde`** feature enabled, it also implements
20/// `Serialize` and `Deserialize` (hex strings in human-readable
21/// formats, raw bytes in binary formats), and can be used with
22/// `#[serde(with = "HexVec")]` on `Vec<u8>` fields.
23///
24/// # Examples
25///
26/// ```
27/// use byte_wrapper::HexVec;
28///
29/// let h = HexVec::new(vec![0x01, 0x02, 0xab, 0xff]);
30/// assert_eq!(h.to_string(), "0102abff");
31///
32/// let parsed: HexVec = "0102abff".parse().unwrap();
33/// assert_eq!(parsed, h);
34/// ```
35#[must_use]
36#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
37pub struct HexVec(pub Vec<u8>);
38
39impl HexVec {
40    /// Creates a new `HexVec` from a byte vector.
41    #[inline]
42    pub fn new(bytes: Vec<u8>) -> Self {
43        Self(bytes)
44    }
45
46    /// Returns the inner byte vector.
47    #[inline]
48    #[must_use]
49    pub fn into_inner(self) -> Vec<u8> {
50        self.0
51    }
52
53    #[inline]
54    fn as_hex_slice(&self) -> &HexSlice {
55        HexSlice::new(&self.0)
56    }
57}
58
59impl FromStr for HexVec {
60    type Err = ParseHexError;
61
62    fn from_str(s: &str) -> Result<Self, Self::Err> {
63        if !s.len().is_multiple_of(2) {
64            return Err(ParseHexError::OddLength { actual: s.len() });
65        }
66        let mut out = vec![0u8; s.len() / 2];
67        decode_hex_into(s, &mut out)?;
68        Ok(Self(out))
69    }
70}
71
72impl fmt::Debug for HexVec {
73    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74        f.debug_tuple("HexVec").field(&HexDisplay(&self.0)).finish()
75    }
76}
77
78impl fmt::Display for HexVec {
79    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80        fmt::Display::fmt(self.as_hex_slice(), f)
81    }
82}
83
84impl fmt::LowerHex for HexVec {
85    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86        fmt::LowerHex::fmt(self.as_hex_slice(), f)
87    }
88}
89
90impl fmt::UpperHex for HexVec {
91    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92        fmt::UpperHex::fmt(self.as_hex_slice(), f)
93    }
94}
95
96impl core::ops::Deref for HexVec {
97    type Target = HexSlice;
98
99    #[inline]
100    fn deref(&self) -> &Self::Target {
101        self.as_hex_slice()
102    }
103}
104
105impl Borrow<HexSlice> for HexVec {
106    #[inline]
107    fn borrow(&self) -> &HexSlice {
108        self.as_hex_slice()
109    }
110}
111
112impl ToOwned for HexSlice {
113    type Owned = HexVec;
114
115    #[inline]
116    fn to_owned(&self) -> Self::Owned {
117        HexVec(self.as_bytes().to_vec())
118    }
119}
120
121impl AsRef<HexSlice> for HexVec {
122    #[inline]
123    fn as_ref(&self) -> &HexSlice {
124        self.as_hex_slice()
125    }
126}
127
128impl AsRef<[u8]> for HexVec {
129    #[inline]
130    fn as_ref(&self) -> &[u8] {
131        &self.0
132    }
133}
134
135impl AsMut<[u8]> for HexVec {
136    #[inline]
137    fn as_mut(&mut self) -> &mut [u8] {
138        &mut self.0
139    }
140}
141
142impl From<Vec<u8>> for HexVec {
143    #[inline]
144    fn from(bytes: Vec<u8>) -> Self {
145        Self(bytes)
146    }
147}
148
149impl From<HexVec> for Vec<u8> {
150    #[inline]
151    fn from(hex_vec: HexVec) -> Self {
152        hex_vec.0
153    }
154}
155
156impl PartialEq<HexSlice> for HexVec {
157    #[inline]
158    fn eq(&self, other: &HexSlice) -> bool {
159        self.as_hex_slice() == other
160    }
161}
162
163impl PartialEq<&HexSlice> for HexVec {
164    #[inline]
165    fn eq(&self, other: &&HexSlice) -> bool {
166        self.as_hex_slice() == *other
167    }
168}
169
170impl PartialEq<HexVec> for HexSlice {
171    #[inline]
172    fn eq(&self, other: &HexVec) -> bool {
173        self == other.as_hex_slice()
174    }
175}
176
177impl PartialEq<HexVec> for &HexSlice {
178    #[inline]
179    fn eq(&self, other: &HexVec) -> bool {
180        *self == other.as_hex_slice()
181    }
182}
183
184#[cfg(feature = "serde")]
185mod serde_impls {
186    use super::HexVec;
187    use crate::HexSlice;
188    use alloc::vec::Vec;
189    use core::fmt;
190    use serde_core::{Deserializer, Serialize, Serializer, de::Visitor};
191
192    fn deserialize_hex_vec<'de, D>(deserializer: D) -> Result<HexVec, D::Error>
193    where
194        D: Deserializer<'de>,
195    {
196        use serde_core::de::Error;
197
198        if deserializer.is_human_readable() {
199            struct HexVecVisitor;
200
201            impl<'de2> Visitor<'de2> for HexVecVisitor {
202                type Value = HexVec;
203
204                fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
205                    write!(f, "a hex string with an even number of hex digits")
206                }
207
208                fn visit_str<E>(self, data: &str) -> Result<Self::Value, E>
209                where
210                    E: Error,
211                {
212                    data.parse().map_err(Error::custom)
213                }
214            }
215
216            deserializer.deserialize_str(HexVecVisitor)
217        } else {
218            crate::serde_util::deserialize_byte_buf(deserializer).map(HexVec)
219        }
220    }
221
222    impl HexVec {
223        /// Serializes a byte vector as hex if the format is
224        /// human-readable, or as raw bytes otherwise.
225        ///
226        /// Intended for use with `#[serde(with = "HexVec")]`.
227        ///
228        /// # Examples
229        ///
230        /// ```
231        /// use byte_wrapper::HexVec;
232        /// use serde::{Deserialize, Serialize};
233        ///
234        /// #[derive(Serialize, Deserialize)]
235        /// struct Blob {
236        ///     #[serde(with = "HexVec")]
237        ///     data: Vec<u8>,
238        /// }
239        ///
240        /// let b = Blob { data: vec![1, 2, 3] };
241        /// let json = serde_json::to_string(&b).unwrap();
242        /// assert_eq!(json, r#"{"data":"010203"}"#);
243        /// ```
244        #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
245        pub fn serialize<S>(
246            bytes: &[u8],
247            serializer: S,
248        ) -> Result<S::Ok, S::Error>
249        where
250            S: Serializer,
251        {
252            HexSlice::new(bytes).serialize(serializer)
253        }
254
255        /// Deserializes a byte vector from hex if the format is
256        /// human-readable, or as raw bytes otherwise.
257        ///
258        /// Intended for use with `#[serde(with = "HexVec")]`.
259        ///
260        /// # Examples
261        ///
262        /// ```
263        /// use byte_wrapper::HexVec;
264        /// use serde::{Deserialize, Serialize};
265        ///
266        /// #[derive(Serialize, Deserialize)]
267        /// struct Blob {
268        ///     #[serde(with = "HexVec")]
269        ///     data: Vec<u8>,
270        /// }
271        ///
272        /// let b: Blob = serde_json::from_str(r#"{"data":"010203"}"#).unwrap();
273        /// assert_eq!(b.data, [1, 2, 3]);
274        /// ```
275        #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
276        pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
277        where
278            D: Deserializer<'de>,
279        {
280            deserialize_hex_vec(deserializer).map(HexVec::into_inner)
281        }
282    }
283
284    #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
285    impl serde_core::Serialize for HexVec {
286        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
287        where
288            S: Serializer,
289        {
290            self.as_hex_slice().serialize(serializer)
291        }
292    }
293
294    #[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
295    impl<'de> serde_core::Deserialize<'de> for HexVec {
296        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
297        where
298            D: Deserializer<'de>,
299        {
300            deserialize_hex_vec(deserializer)
301        }
302    }
303}
304
305#[cfg(feature = "schemars08")]
306mod schemars_impls {
307    use super::HexVec;
308    use crate::{HexSlice, schemars_util::x_rust_type_extension};
309    use alloc::{boxed::Box, string::String};
310    use schemars08::{
311        JsonSchema,
312        r#gen::SchemaGenerator,
313        schema::{InstanceType, Schema, SchemaObject, StringValidation},
314    };
315
316    impl JsonSchema for HexVec {
317        fn schema_name() -> String {
318            "HexVec".into()
319        }
320
321        fn is_referenceable() -> bool {
322            false
323        }
324
325        fn json_schema(_generator: &mut SchemaGenerator) -> Schema {
326            Schema::Object(SchemaObject {
327                instance_type: Some(InstanceType::String.into()),
328                string: Some(Box::new(StringValidation {
329                    pattern: Some("^([0-9a-fA-F]{2})*$".into()),
330                    ..Default::default()
331                })),
332                extensions: x_rust_type_extension("HexVec"),
333                ..Default::default()
334            })
335        }
336    }
337
338    // HexSlice shares HexVec's schema (including `x-rust-type` set to
339    // `HexVec`) since generated code needs owned types. (This also covers
340    // `Cow<'_, HexSlice>`.)
341    impl JsonSchema for HexSlice {
342        fn schema_name() -> String {
343            HexVec::schema_name()
344        }
345
346        fn is_referenceable() -> bool {
347            HexVec::is_referenceable()
348        }
349
350        fn json_schema(generator: &mut SchemaGenerator) -> Schema {
351            HexVec::json_schema(generator)
352        }
353    }
354}