serde_hex/macros/
hex.rs

1//! Various helpful macros related to implementing `SerHex`.
2
3/// implement `SerHexSeq` for a specified type.
4#[macro_export]
5macro_rules! impl_serhex_seq {
6    ($type: ty, $bytes: expr) => {
7        impl $crate::SerHexSeq<$crate::Strict> for $type {
8            fn size() -> usize {
9                $bytes
10            }
11        }
12
13        impl $crate::SerHexSeq<$crate::StrictPfx> for $type {
14            fn size() -> usize {
15                $bytes
16            }
17        }
18
19        impl $crate::SerHexSeq<$crate::StrictCap> for $type {
20            fn size() -> usize {
21                $bytes
22            }
23        }
24
25        impl $crate::SerHexSeq<$crate::StrictCapPfx> for $type {
26            fn size() -> usize {
27                $bytes
28            }
29        }
30    };
31}
32
33/// helper macro for implementing the `into_hex_raw` function for
34/// bytearray-style types.
35#[doc(hidden)]
36#[macro_export]
37macro_rules! into_hex_bytearray {
38    ($src: ident, $dst: ident, $len: expr) => {{
39        let src: &[u8] = $src.as_ref();
40        debug_assert!(src.len() == $len);
41        // add prefix if we are doing such things.
42        if <C as $crate::HexConf>::withpfx() {
43            $dst.write_all("0x".as_bytes())?;
44        }
45        // if
46        if <C as $crate::HexConf>::compact() {
47            // find index and location of first non-zero byte.
48            if let Some((idx, val)) = src.iter().enumerate().find(|&(_, v)| *v > 0u8) {
49                // if first non-zero byte is less than `0x10`, repr w/ one hex char.
50                if *val < 0x10 {
51                    if <C as $crate::HexConf>::withcap() {
52                        $dst.write_all(&[$crate::utils::fromvalcaps(*val)])?;
53                        $crate::utils::writehexcaps(&src[(idx + 1)..], $dst)
54                    } else {
55                        $dst.write_all(&[$crate::utils::fromval(*val)])?;
56                        $crate::utils::writehex(&src[(idx + 1)..], $dst)
57                    }
58                } else {
59                    if <C as $crate::HexConf>::withcap() {
60                        $crate::utils::writehexcaps(&src[idx..], $dst)
61                    } else {
62                        $crate::utils::writehex(&src[idx..], $dst)
63                    }
64                }
65            // if no non-zero byte was found, just write in a zero.
66            } else {
67                $dst.write_all(&[b'0'])?;
68                Ok(())
69            }
70        } else {
71            if <C as $crate::HexConf>::withcap() {
72                $crate::utils::writehexcaps(src, $dst)
73            } else {
74                $crate::utils::writehex(src, $dst)
75            }
76        }
77    }};
78}
79
80/// helper macro for implementing the `into_hex_raw` function for
81/// bytearray-style types.
82#[doc(hidden)]
83#[macro_export]
84macro_rules! from_hex_bytearray {
85    ($src: ident, $len: expr) => {{
86        let raw: &[u8] = $src.as_ref();
87        let hex = if <C as $crate::HexConf>::withpfx() {
88            let pfx = "0x".as_bytes();
89            if raw.starts_with(pfx) {
90                &raw[2..]
91            } else {
92                raw
93            }
94        } else {
95            raw
96        };
97        let mut buf = [0u8; $len];
98        if <C as $crate::HexConf>::compact() {
99            let min = 1;
100            let max = $len * 2;
101            let got = hex.len();
102            if got < min || got > max {
103                let inner = $crate::types::ParseHexError::Range { min, max, got };
104                let error = $crate::types::Error::from(inner);
105                return Err(error.into());
106            }
107            let body = $len - (got / 2);
108            let head = got % 2;
109            if head > 0 {
110                buf[body - head] = $crate::utils::intobyte(b'0', hex[0])?;
111            }
112            $crate::utils::fromhex(&mut buf[body..], &hex[head..])?;
113        } else {
114            $crate::utils::fromhex(&mut buf[..], hex)?;
115        }
116        Ok(buf)
117    }};
118}
119
120/// macro for implementing `SerHex` for a type which implements
121/// `From<[u8;n]>` and `AsRef<[u8]>`.
122#[macro_export]
123macro_rules! impl_serhex_bytearray {
124    ($type: ty, $len: expr) => {
125        impl_serhex_seq!($type, $len);
126        impl<C> $crate::SerHex<C> for $type
127        where
128            C: $crate::HexConf,
129        {
130            type Error = $crate::types::Error;
131            fn into_hex_raw<D>(&self, mut dst: D) -> ::std::result::Result<(), Self::Error>
132            where
133                D: ::std::io::Write,
134            {
135                into_hex_bytearray!(self, dst, $len)?;
136                Ok(())
137            }
138            fn from_hex_raw<S>(src: S) -> ::std::result::Result<Self, Self::Error>
139            where
140                S: AsRef<[u8]>,
141            {
142                let rslt: ::std::result::Result<[u8; $len], Self::Error> =
143                    from_hex_bytearray!(src, $len);
144                match rslt {
145                    Ok(buf) => Ok(buf.into()),
146                    Err(e) => Err(e),
147                }
148            }
149        }
150    };
151}
152
153#[cfg(test)]
154mod tests {
155    use {
156        Compact, CompactCap, CompactCapPfx, CompactPfx, SerHex, Strict, StrictCap, StrictCapPfx,
157        StrictPfx,
158    };
159
160    #[derive(Debug, PartialEq, Eq)]
161    struct Foo([u8; 4]);
162    impl_newtype_bytearray!(Foo, 4);
163    impl_serhex_bytearray!(Foo, 4);
164
165    #[test]
166    fn hex_strict_ok() {
167        let f1 = Foo([0, 1, 2, 3]);
168        let hs = <Foo as SerHex<Strict>>::into_hex(&f1).unwrap();
169        let f2 = <Foo as SerHex<Strict>>::from_hex(&hs).unwrap();
170        assert_eq!(f1, f2);
171    }
172
173    #[test]
174    #[should_panic]
175    fn hex_strict_err() {
176        let _ = <Foo as SerHex<Strict>>::from_hex("faaffaa").unwrap();
177    }
178
179    #[test]
180    fn hex_compact() {
181        let f1 = Foo([0, 0, 0x0a, 0xff]);
182        let hs = <Foo as SerHex<Compact>>::into_hex(&f1).unwrap();
183        assert_eq!(&hs, "aff");
184        let f2 = <Foo as SerHex<Compact>>::from_hex(&hs).unwrap();
185        assert_eq!(f1, f2);
186    }
187
188    #[test]
189    fn hex_variants() {
190        let f = Foo([0x00, 0x0f, 0xff, 0x11]);
191        assert_eq!(
192            "0x000fff11",
193            <Foo as SerHex<StrictPfx>>::into_hex(&f).unwrap()
194        );
195        assert_eq!(
196            "000FFF11",
197            <Foo as SerHex<StrictCap>>::into_hex(&f).unwrap()
198        );
199        assert_eq!(
200            "0x000FFF11",
201            <Foo as SerHex<StrictCapPfx>>::into_hex(&f).unwrap()
202        );
203        assert_eq!(
204            "0xfff11",
205            <Foo as SerHex<CompactPfx>>::into_hex(&f).unwrap()
206        );
207        assert_eq!("FFF11", <Foo as SerHex<CompactCap>>::into_hex(&f).unwrap());
208        assert_eq!(
209            "0xFFF11",
210            <Foo as SerHex<CompactCapPfx>>::into_hex(&f).unwrap()
211        );
212    }
213
214    #[test]
215    fn blanket_array() {
216        let v: [Foo; 2] = <[Foo; 2] as SerHex<StrictPfx>>::from_hex("0xffaaffaa11221122").unwrap();
217        assert_eq!(v[0], Foo([0xff, 0xaa, 0xff, 0xaa]));
218        assert_eq!(v[1], Foo([0x11, 0x22, 0x11, 0x22]));
219        let hs = <[Foo; 2] as SerHex<StrictPfx>>::into_hex(&v).unwrap();
220        assert_eq!(hs, "0xffaaffaa11221122");
221    }
222}