byte_array_ops/core/
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#[cfg(test)]
92mod tests {
93    use crate::ByteArray;
94    use crate::errors::ByteArrayError;
95    use alloc::vec;
96
97    #[test]
98    fn test_malformed_input() {
99        let res = try_bytes!("0xZZFEFE").err().unwrap();
100        assert_eq!(res, ByteArrayError::InvalidHexChar('Z'));
101    }
102
103    #[test]
104    fn test_empty_bytes() {
105        let res = try_bytes!("").err().unwrap();
106        assert_eq!(res, ByteArrayError::EmptyInput);
107    }
108
109    #[test]
110    fn test_standard_utf() {
111        let res = try_bytes!("hello").unwrap();
112        assert_eq!(res.as_bytes(), b"hello");
113    }
114
115    #[test]
116    fn test_hex_with_prefix() {
117        let res = try_bytes!("0xDEADBEEF").unwrap();
118        assert_eq!(res.as_bytes(), &[0xDE, 0xAD, 0xBE, 0xEF]);
119    }
120
121    #[test]
122    fn test_hex_without_prefix() {
123        // treated as utf8
124        let res = try_bytes!("DEADBEEF").unwrap();
125        assert_eq!(res.as_bytes(), b"DEADBEEF");
126    }
127
128    #[test]
129    fn test_lowercase_hex() {
130        let res = try_bytes!("0xdeadbeef").unwrap();
131        assert_eq!(res.as_bytes(), &[0xDE, 0xAD, 0xBE, 0xEF]);
132    }
133
134    #[test]
135    fn test_mixed_case_hex() {
136        let res = try_bytes!("0xDeAdBeEf").unwrap();
137        assert_eq!(res.as_bytes(), &[0xDE, 0xAD, 0xBE, 0xEF]);
138    }
139
140    #[test]
141    fn test_single_byte_hex() {
142        let res = try_bytes!("0xFF").unwrap();
143        assert_eq!(res.as_bytes(), &[0xFF]);
144    }
145
146    #[test]
147    fn test_two_byte_hex() {
148        let res = try_bytes!("0xCAFE").unwrap();
149        assert_eq!(res.as_bytes(), &[0xCA, 0xFE]);
150    }
151
152    #[test]
153    fn test_odd_length_hex() {
154        // Test your implementation's behavior with odd-length hex strings
155        let res = try_bytes!("0xFFF");
156        // Adjust assertion based on whether you pad or error
157        assert!(res.is_ok() || res.is_err());
158    }
159
160    #[test]
161    fn test_special_characters_utf8() {
162        let res = try_bytes!("hello world!").unwrap();
163        assert_eq!(res.as_bytes(), b"hello world!");
164    }
165
166    #[test]
167    fn test_unicode_utf8() {
168        let res = try_bytes!("\u{1F980}").unwrap();
169        assert_eq!(res.as_bytes(), &[0xF0, 0x9F, 0xa6, 0x80]);
170    }
171
172    // Test that array construction still works via From/Into
173    #[test]
174    fn test_from_array() {
175        let res = ByteArray::from([0x01, 0x02, 0x03, 0xFF]);
176        assert_eq!(res.as_bytes(), &[0x01, 0x02, 0x03, 0xFF]);
177    }
178
179    #[test]
180    fn test_from_vec() {
181        let res = ByteArray::from(vec![0xDE, 0xAD, 0xBE, 0xEF]);
182        assert_eq!(res.as_bytes(), &[0xDE, 0xAD, 0xBE, 0xEF]);
183    }
184
185    #[test]
186    fn test_from_slice() {
187        let slice: &[u8] = &[0xCA, 0xFE];
188        let res = ByteArray::from(slice);
189        assert_eq!(res.as_bytes(), &[0xCA, 0xFE]);
190    }
191
192    #[test]
193    fn test_try_hex_macro() {
194        let res = try_hex!("deadbeef").unwrap();
195        assert_eq!(res.as_bytes(), &[0xde, 0xad, 0xbe, 0xef]);
196    }
197
198    #[test]
199    fn test_try_bin_macro() {
200        let res = try_bin!("11110000").unwrap();
201        assert_eq!(res.as_bytes(), &[0xf0]);
202    }
203}