byte_array_ops/
macros.rs

1#[doc(hidden)]
2#[macro_export]
3macro_rules! count_args {
4    () => (0usize);
5    ($x:tt $($xs:tt)*) => (1usize + count_args!($($xs)*));
6}
7
8/// Macro for creating a `ByteArray` from a string literal with automatic format detection.
9///
10/// # Format Detection
11/// - **With `0x` prefix**: Parses as hexadecimal
12/// - **With `0b` prefix**: Parses as binary
13/// - **With `0o` prefix**: Parses as octal (not yet implemented)
14/// - **No prefix**: **CAVEAT - Silently converts to UTF-8 encoding**
15///
16/// # CAVEAT
17/// When no format prefix (`0x`, `0b`, `0o`) is provided, this macro will **silently interpret
18/// the input as UTF-8** and convert it to bytes. If you intend to parse hex or binary data,
19/// you **must** include the appropriate prefix, or use `try_hex!` or `try_bin!` instead.
20///
21/// # Examples
22/// ```
23/// use byte_array_ops::try_bytes;
24///
25/// // Hex with prefix
26/// let hex = try_bytes!("0xdeadbeef").unwrap();
27/// assert_eq!(hex.as_bytes(), [0xde, 0xad, 0xbe, 0xef]);
28///
29/// // Binary with prefix
30/// let bin = try_bytes!("0b11110000").unwrap();
31/// assert_eq!(bin.as_bytes(), [0xf0]);
32///
33/// // No prefix - silently converts to UTF-8
34/// let utf8 = try_bytes!("hello").unwrap();
35/// assert_eq!(utf8.as_bytes(), b"hello");
36/// ```
37#[macro_export]
38macro_rules! try_bytes {
39    ($x:literal) => {{
40        use core::str::FromStr;
41        use $crate::ByteArray;
42
43        ByteArray::from_str($x)
44    }};
45}
46
47/// Macro for creating a `ByteArray` from a hexadecimal string literal (without `0x` prefix).
48///
49/// # CAVEAT
50/// This macro **always** interprets the input as hexadecimal. It does **not** perform UTF-8
51/// conversion. If you need automatic format detection with UTF-8 fallback, use [`try_bytes!`] instead.
52///
53/// # Examples
54/// ```
55/// use byte_array_ops::try_hex;
56///
57/// let hex = try_hex!("deadbeef").unwrap();
58/// assert_eq!(hex.as_bytes(), [0xde, 0xad, 0xbe, 0xef]);
59/// ```
60#[macro_export]
61macro_rules! try_hex {
62    ($x:literal) => {{
63        use $crate::ByteArray;
64
65        ByteArray::from_hex($x)
66    }};
67}
68
69/// Macro for creating a `ByteArray` from a binary string literal (without `0b` prefix).
70///
71/// # CAVEAT
72/// This macro **always** interprets the input as binary. It does **not** perform UTF-8
73/// conversion. If you need automatic format detection with UTF-8 fallback, use [`try_bytes!`] instead.
74///
75/// # Examples
76/// ```
77/// use byte_array_ops::try_bin;
78///
79/// let bin = try_bin!("11110000").unwrap();
80/// assert_eq!(bin.as_bytes(), [0xf0]);
81/// ```
82#[macro_export]
83macro_rules! try_bin {
84    ($x:literal) => {{
85        use $crate::ByteArray;
86
87        ByteArray::from_bin($x)
88    }};
89}
90
91/// Macro to create a [`ByteArray`] with a value and a count similar to `vec![val; count]`
92///
93/// # Examples
94///```
95/// use byte_array_ops::bytes;
96///
97/// let bytes = bytes![5;10];
98///
99/// assert_eq!(bytes.as_bytes(),&vec![5u8;10]);
100///```
101#[macro_export]
102macro_rules! bytes {
103    [$pat:expr;$len:expr] => {{
104        use $crate::ByteArray;
105
106        ByteArray::init_value($pat,$len)
107    }};
108}
109
110#[cfg(test)]
111mod tests {
112    use crate::ByteArray;
113    use crate::errors::ByteArrayError;
114    use alloc::vec;
115
116    #[test]
117    fn test_malformed_input() {
118        let res = try_bytes!("0xZZFEFE").err().unwrap();
119        assert_eq!(res, ByteArrayError::InvalidHexChar('Z'));
120    }
121
122    #[test]
123    fn test_empty_bytes() {
124        let res = try_bytes!("").err().unwrap();
125        assert_eq!(res, ByteArrayError::EmptyInput);
126    }
127
128    #[test]
129    fn test_standard_utf() {
130        let res = try_bytes!("hello").unwrap();
131        assert_eq!(res.as_bytes(), b"hello");
132    }
133
134    #[test]
135    fn test_hex_with_prefix() {
136        let res = try_bytes!("0xDEADBEEF").unwrap();
137        assert_eq!(res.as_bytes(), &[0xDE, 0xAD, 0xBE, 0xEF]);
138    }
139
140    #[test]
141    fn test_hex_without_prefix() {
142        // treated as utf8
143        let res = try_bytes!("DEADBEEF").unwrap();
144        assert_eq!(res.as_bytes(), b"DEADBEEF");
145    }
146
147    #[test]
148    fn test_lowercase_hex() {
149        let res = try_bytes!("0xdeadbeef").unwrap();
150        assert_eq!(res.as_bytes(), &[0xDE, 0xAD, 0xBE, 0xEF]);
151    }
152
153    #[test]
154    fn test_mixed_case_hex() {
155        let res = try_bytes!("0xDeAdBeEf").unwrap();
156        assert_eq!(res.as_bytes(), &[0xDE, 0xAD, 0xBE, 0xEF]);
157    }
158
159    #[test]
160    fn test_single_byte_hex() {
161        let res = try_bytes!("0xFF").unwrap();
162        assert_eq!(res.as_bytes(), &[0xFF]);
163    }
164
165    #[test]
166    fn test_two_byte_hex() {
167        let res = try_bytes!("0xCAFE").unwrap();
168        assert_eq!(res.as_bytes(), &[0xCA, 0xFE]);
169    }
170
171    #[test]
172    fn test_odd_length_hex() {
173        // Test your implementation's behavior with odd-length hex strings
174        let res = try_bytes!("0xFFF");
175        // Adjust assertion based on whether you pad or error
176        assert!(res.is_ok() || res.is_err());
177    }
178
179    #[test]
180    fn test_special_characters_utf8() {
181        let res = try_bytes!("hello world!").unwrap();
182        assert_eq!(res.as_bytes(), b"hello world!");
183    }
184
185    #[test]
186    fn test_unicode_utf8() {
187        let res = try_bytes!("\u{1F980}").unwrap();
188        assert_eq!(res.as_bytes(), &[0xF0, 0x9F, 0xa6, 0x80]);
189    }
190
191    // Test that array construction still works via From/Into
192    #[test]
193    fn test_from_array() {
194        let res = ByteArray::from([0x01, 0x02, 0x03, 0xFF]);
195        assert_eq!(res.as_bytes(), &[0x01, 0x02, 0x03, 0xFF]);
196    }
197
198    #[test]
199    fn test_from_vec() {
200        let res = ByteArray::from(vec![0xDE, 0xAD, 0xBE, 0xEF]);
201        assert_eq!(res.as_bytes(), &[0xDE, 0xAD, 0xBE, 0xEF]);
202    }
203
204    #[test]
205    fn test_from_slice() {
206        let slice: &[u8] = &[0xCA, 0xFE];
207        let res = ByteArray::from(slice);
208        assert_eq!(res.as_bytes(), &[0xCA, 0xFE]);
209    }
210
211    #[test]
212    fn test_try_hex_macro() {
213        let res = try_hex!("deadbeef").unwrap();
214        assert_eq!(res.as_bytes(), &[0xde, 0xad, 0xbe, 0xef]);
215    }
216
217    #[test]
218    fn test_try_bin_macro() {
219        let res = try_bin!("11110000").unwrap();
220        assert_eq!(res.as_bytes(), &[0xf0]);
221    }
222
223    // test bytes empty
224    // test bytes 0 0
225    // test bytes pat 0
226    // test bytes 0 count
227
228    #[test]
229    fn test_bytes_macro_zeros() {
230        let bytes = bytes![0;0];
231
232        assert_eq!(bytes, ByteArray::default());
233    }
234
235    #[test]
236    fn test_bytes_macro_pat_zero() {
237        let bytes = bytes![8u8;0];
238
239        assert_eq!(bytes, ByteArray::default());
240    }
241
242    #[test]
243    fn test_bytes_macro_general() {
244        let bytes = bytes![0xff;1065];
245
246        assert_eq!(bytes.as_bytes(), vec![0xff; 1065]);
247    }
248}