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}