Skip to main content

hex_magic/
lib.rs

1//! This crate provides macros for working with bytes and hexadecimal values.
2//!
3//! # `hex!`
4//!
5//! [`hex!`](hex!) is a macro which converts string literals (`"7D2B"`) to byte arrays (`[0x7D, 0x2B]`) or match patterns at compile time.
6//!
7//! ```
8//! assert_eq!(hex!("01020304"), [1, 2, 3, 4]);
9//! ```
10//! # `parse_struct!`
11//!
12//! [`parse_struct!`](parse_struct!) is a macro for parsing bytes from [`Read`](std::io::Read) readers
13//! into structs (or enums), with the ability to skip padding bytes.
14//! It returns a `Result<Struct, std::io::Error>` value.
15//!
16//! ```
17//! use hex_magic::parse_struct;
18//! use std::io::{Read, Result};
19//!
20//! #[derive(Debug)]
21//! struct Data {
22//!     a: [u8; 2],
23//!     b: u32,
24//! }
25//!
26//! fn main() -> Result<()> {
27//!     let bytes = [
28//!         0x48, 0x45, 0x58, 0x00, 0x01, 0x02, 0x00, 0xAA, 0xBB, 0xCC, 0xDD,
29//!     ];
30//!     let data = parse_struct!(bytes.as_ref() => Data {
31//!         _: b"HEX",
32//!         _: [0],
33//!         a: [0x01, _],
34//!         _: "00",
35//!         b: buf @ "AABB ____" => u32::from_le_bytes(*buf),
36//!     })?;
37//!     println!("{:X?}", data); // Data { a: [1, 2], b: DDCCBBAA }
38//!     Ok(())
39//! }
40//! ```
41use proc_macro::TokenStream;
42use quote::quote;
43use syn::parse_macro_input;
44
45mod hex_string;
46mod parse_struct;
47use hex_string::HexString;
48use parse_struct::HexStruct;
49
50/// Macro which converts string literals (`"7D2B"`) to byte arrays (`[0x7D, 0x2B]`) at compile time.
51///
52/// It's a rewrite of the `hex!` macro provided by the [`hex-literal`](https://docs.rs/hex-literal/) crate
53/// with stricter rules requiring bytes to come in pairs (so `"12 34"` is allowed but `"1 2 3 4"` is
54/// not) and with the addition of being able to parse `__` and `..` to create match patterns.
55///
56/// It accepts the following characters in the input string:
57///
58/// - `'0'...'9'`, `'a'...'f'`, `'A'...'F'` -- hex characters which will be used
59///     in construction of the output byte array
60/// - `' '`, `'\r'`, `'\n'`, `'\t'` -- formatting characters which will be
61///     ignored
62/// - `'_'`, `'.'` -- formatting characters which will be used to create match patterns
63///
64/// # Example
65///
66/// ```
67/// use hex_magic::hex;
68///
69/// const BYTES: [u8; 3] = hex!("DEAD AF");
70///
71/// fn main() {
72///     assert_eq!(BYTES, [0xDE, 0xAD, 0xAF]);
73///     assert_eq!(hex!("aA aa aA Aa aa"), [0xAA; 5]);
74///
75///     match [1, 2, 3, 4] {
76///         hex!("AABBCCDD") => panic!("bytes don't match at all"),
77///         hex!("01__FF__") => panic!("[1, _, 0xFF, _] does not match"),
78///         hex!("01..04") => println!("[1, .., 4] would match"),
79///         hex!("..") => unreachable!("[..] would match"),
80///     }
81/// }
82/// ```
83#[proc_macro]
84pub fn hex(stream: TokenStream) -> TokenStream {
85    let input = parse_macro_input!(stream as HexString);
86    TokenStream::from(quote!(#input))
87}
88
89/// Macro for parsing bytes from [`Read`](std::io::Read) readers into structs
90/// with the ability to skip padding bytes.
91///
92/// # Syntax
93///
94/// ```
95/// parse_struct!(READER => STRUCT {
96///     ...
97///     FIELD: [BINDING @] BYTE_PATTERN [=> EXPRESSION],
98///     ...
99/// })
100/// ```
101///
102/// First, the macro expects a reader or an expression the result of which would be a reader.
103/// The reader is followed by `=>` and then by a modified form of struct instantiation.
104///
105/// The basic syntax of struct instantiation takes the form of `FIELD: BYTE_PATTERN`. This will assign
106/// the read bytes (`[u8; N]`) to the given field if it matches the pattern.
107/// For more advanced scenarios, such as for converting the bytes to other types,
108/// bindings and expressions can be used:
109/// `FIELD: BINDING @ BYTE_PATTERN => EXPRESSION`.
110/// In this case, the result of `EXPRESSION` will be assigned to `FIELD`.
111///
112/// A special `_` field is available for matching against bytes without including them in the
113/// struct. `_` fields can be specified multiple times and
114/// can be used for skipping padding bytes or for matching against bytes without including them in
115/// the struct. Bindings and expressions can be used with these fields as well but expressions must
116/// evaluate to ().
117///
118/// Patterns can be any of:
119/// - `[1, 2, 3, _, 5]` - standard byte array patterns
120/// - `b"byte string!"` - byte strings
121/// - `"FF00FF 00FF00"` - hex strings usable with the [`hex!`](hex!) macro
122///
123/// Patterns can include `_` but not `..` wildcards since the length of the pattern is
124/// used to determine the amount of bytes to read.
125///
126/// Structs or enum variants with unnamed members (`Item(A, B)`) can be used with the
127/// `Struct { 0: ..., 1: ... }` syntax.
128///
129/// This macro returns `Result` containing either the resulting struct
130/// or [`std::io::Error`](std::io::Error) if an error occurred while reading or matching the bytes.
131/// [`std::io::ErrorKind::InvalidData`](std::io::ErrorKind::InvalidData)
132/// if the bytes were not matched successfully.
133///
134/// # Example
135///
136/// ```
137/// use hex_magic::parse_struct;
138/// use std::io::{Read, Result};
139///
140/// #[derive(Debug)]
141/// struct Data {
142///     a: [u8; 2],
143///     b: u32,
144/// }
145///
146/// fn main() -> Result<()> {
147///     let bytes = [
148///         0x48, 0x45, 0x58, 0x00, 0x01, 0x02, 0x00, 0xAA, 0xBB, 0xCC, 0xDD,
149///     ];
150///     let data = parse_struct!(bytes.as_ref() => Data {
151///         _: b"HEX",
152///         _: [0],
153///         a: [0x01, _],
154///         _: "00",
155///         b: buf @ "AABB ____" => u32::from_le_bytes(*buf),
156///     })?;
157///     println!("{:X?}", data); // Data { a: [1, 2], b: DDCCBBAA }
158///     Ok(())
159/// }
160/// ```
161///
162/// # Details
163///
164/// This macro would be parsed into a closure which is instantly called so that any
165/// potential errors caused by `Read` can be handled explicitly by the user.
166///
167/// The macro in the example above would be parsed into the following code
168/// (internal variable names prefixed with `_` changed for clarity):
169///
170/// ```
171/// (|| {
172///     use std::convert::TryInto;
173///     #[allow(non_snake_case)]
174///     let mut _READER = bytes.as_ref();
175///     #[allow(non_snake_case)]
176///     let mut _ARRAY: [u8; 4usize] = [0; 4usize]; // length of the longest pattern
177///     let _: () = {
178///         _READER.read_exact(&mut _ARRAY[0..3usize])?;
179///         #[allow(non_snake_case)]
180///         let _BUFFER: &[u8; 3usize] = _ARRAY[0..3usize].try_into().unwrap();
181///         #[allow(dead_code)]
182///         match _BUFFER {
183///             [72u8, 69u8, 88u8] => (), // b"HEX"
184///             _ => {
185///                 return Err(std::io::Error::new(
186///                     std::io::ErrorKind::InvalidData,
187///                     format!("expected `{}`, got `{:02X?}`", "b\"HEX\"", _BUFFER),
188///                 ))
189///             }
190///         }
191///     };
192///     let _: () = {
193///         _READER.read_exact(&mut _ARRAY[0..1usize])?;
194///         #[allow(non_snake_case)]
195///         let _BUFFER: &[u8; 1usize] = _ARRAY[0..1usize].try_into().unwrap();
196///         #[allow(dead_code)]
197///         match _BUFFER {
198///             [0] => (),
199///             _ => {
200///                 return Err(std::io::Error::new(
201///                     std::io::ErrorKind::InvalidData,
202///                     format!("expected `{}`, got `{:02X?}`", "[0]", _BUFFER),
203///                 ))
204///             }
205///         }
206///     };
207///     let _a = {
208///         _READER.read_exact(&mut _ARRAY[0..2usize])?;
209///         #[allow(non_snake_case)]
210///         let _BUFFER: &[u8; 2usize] = _ARRAY[0..2usize].try_into().unwrap();
211///         #[allow(dead_code)]
212///         match _BUFFER {
213///             [0x01, _] => (),
214///             _ => {
215///                 return Err(std::io::Error::new(
216///                     std::io::ErrorKind::InvalidData,
217///                     format!("expected `{}`, got `{:02X?}`", "[0x01, _]", _BUFFER),
218///                 ))
219///             }
220///         }
221///         *_BUFFER // [u8; 2] as the result if no expression given
222///     };
223///     let _: () = {
224///         _READER.read_exact(&mut _ARRAY[0..1usize])?;
225///         #[allow(non_snake_case)]
226///         let _BUFFER: &[u8; 1usize] = _ARRAY[0..1usize].try_into().unwrap();
227///         #[allow(dead_code)]
228///         match _BUFFER {
229///             [0u8] => (),
230///             _ => {
231///                 return Err(std::io::Error::new(
232///                     std::io::ErrorKind::InvalidData,
233///                     format!("expected `{}`, got `{:02X?}`", "[0x00]", _BUFFER),
234///                 ))
235///             }
236///         }
237///     };
238///     let _b = {
239///         _READER.read_exact(&mut _ARRAY[0..4usize])?;
240///         #[allow(non_snake_case)]
241///         let buf: &[u8; 4usize] = _ARRAY[0..4usize].try_into().unwrap(); // assign binding
242///         #[allow(dead_code)]
243///         match buf {
244///             [170u8, 187u8, _, _] => (), // "AABB ____"
245///             _ => {
246///                 return Err(std::io::Error::new(
247///                     std::io::ErrorKind::InvalidData,
248///                     format!("expected `{}`, got `{:02X?}`", "[0xAA, 0xBB, _, _]", buf),
249///                 ))
250///             }
251///         }
252///         u32::from_le_bytes(*buf) // provided expression
253///     };
254///     Ok(Data { a: _a, b: _b }) // `_` fields are not included in the resulting struct
255/// })()
256/// ```
257#[proc_macro]
258pub fn parse_struct(stream: TokenStream) -> TokenStream {
259    let input = parse_macro_input!(stream as HexStruct);
260    TokenStream::from(quote!(#input))
261}