#[doc(hidden)]
pub trait ParseStr {
fn parse_str(line: usize, to_parse: &'static str) -> Self;
}
impl ParseStr for Vec<u8> {
fn parse_str(line: usize, to_parse: &'static str) -> Self {
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)
}
};
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))
}
}
#[macro_export] macro_rules! read_test_vectors {
($path:expr => $struct:ident { line, $($field_name:ident),+ }) => ({
fn parse_line<T: ParseStr>(key: &str, (line, text): (usize, &'static str)) -> T {
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)
};
T::parse_str(line, text)
}
let mut lines = include_str!($path).lines().enumerate()
.filter(|(_, line)| !line.is_empty() && !line.starts_with('#'))
.peekable();
let mut structs = Vec::new();
loop {
let line = match lines.peek() {
Some((line, _)) => *line + 1,
None => break structs
};
structs.push($struct {
line,
$($field_name: parse_line(
stringify!($field_name),
lines.next().unwrap_or_else(|| panic!("Incomplete test vector @{}", line))
)),+
})
}
});
}