/// A trait to parse the test-vector strings #[doc(hidden)] pub trait ParseStr { /// Parses the string `to_parse` at `line` to `Self` or panics fn parse_str(line: usize, to_parse: &'static str) -> Self; } impl ParseStr for Vec { fn parse_str(line: usize, to_parse: &'static str) -> Self { // Helper to decode a hex-encoded nibble let decode = |nibble: u8| -> u8 { match nibble { n @ b'0'..=b'9' => n - b'0', n @ b'a'..=b'f' => (n - b'a') + 10, n @ b'A'..=b'F' => (n - b'A') + 10, n => panic!("Test vector contains invalid hex char \"{:02x}\" @{}", n, line) } }; // Decode the string assert_eq!(to_parse.len() % 2, 0, "Test vector contains invalid hex string @{}", line); to_parse.as_bytes().chunks(2) .fold(Self::new(), |mut vec, pair| { vec.push(decode(pair[0]) << 4 | decode(pair[1])); vec }) } } impl ParseStr for &'static str { fn parse_str(_line: usize, to_parse: &'static str) -> Self { to_parse } } impl ParseStr for usize { fn parse_str(line: usize, to_parse: &'static str) -> Self { use std::str::FromStr; usize::from_str(to_parse) .unwrap_or_else(|_| panic!("Test vector contains invalid usize @{}", line)) } } /// Reads the test vectors at `$path` #[macro_export] macro_rules! read_test_vectors { ($path:expr => $struct:ident { line, $($field_name:ident),+ }) => ({ // Helper to parse a line fn parse_line(key: &str, (line, text): (usize, &'static str)) -> T { // Validate that the lines starts with `key` and extracts the value-substring let key = format!("{}:", key); let text = match text.starts_with(&key) { true => text.split_at(key.len()).1.trim(), false => panic!("Invalid line-key @{}", line) }; // Decode the string T::parse_str(line, text) } // Enumerate the lines and ignore empty and comment lines let mut lines = include_str!($path).lines().enumerate() .filter(|(_, line)| !line.is_empty() && !line.starts_with('#')) .peekable(); // Let line number let mut structs = Vec::new(); loop { // Check if we have a next test vector let line = match lines.peek() { Some((line, _)) => *line + 1, None => break structs }; // Parse test vector structs.push($struct { line, $($field_name: parse_line( stringify!($field_name), lines.next().unwrap_or_else(|| panic!("Incomplete test vector @{}", line)) )),+ }) } }); }