#![cfg(test)]
use alloc::string::String;
use alloc::vec::Vec;
use std::collections::HashMap;
pub(crate) fn vector_path(rel: &str) -> String {
alloc::format!("{}/tests/vectors/{}", env!("CARGO_MANIFEST_DIR"), rel)
}
pub(crate) fn hex_to_bytes(hex: &str) -> Vec<u8> {
let hex = hex.trim();
(0..hex.len())
.step_by(2)
.map(|i| u8::from_str_radix(&hex[i..i + 2], 16).unwrap())
.collect()
}
pub(crate) fn hex_to_32(hex: &str) -> [u8; 32] {
let v = hex_to_bytes(hex);
let mut a = [0u8; 32];
a.copy_from_slice(&v);
a
}
#[derive(Debug, Clone)]
pub(crate) enum Value {
Null,
Bool(bool),
Number(f64),
Str(String),
Array(Vec<Value>),
Object(HashMap<String, Value>),
}
impl Value {
pub(crate) fn get_str(&self, key: &str) -> &str {
match self {
Value::Object(map) => match map.get(key) {
Some(Value::Str(s)) => s,
_ => "",
},
_ => "",
}
}
pub(crate) fn get_array(&self, key: &str) -> &[Value] {
match self {
Value::Object(map) => match map.get(key) {
Some(Value::Array(a)) => a,
_ => panic!("Key '{key}' not found or not an array"),
},
_ => panic!("Not an object"),
}
}
pub(crate) fn get_u64(&self, key: &str) -> u64 {
match self {
Value::Object(map) => match map.get(key) {
Some(Value::Number(n)) => *n as u64,
_ => panic!("Key '{key}' not found or not a number"),
},
_ => panic!("Not an object"),
}
}
pub(crate) fn has(&self, key: &str) -> bool {
matches!(self, Value::Object(map) if map.contains_key(key))
}
pub(crate) fn get(&self, key: &str) -> Option<&Value> {
match self {
Value::Object(map) => map.get(key),
_ => None,
}
}
}
pub(crate) fn read_json(path: &str) -> Value {
let content = std::fs::read_to_string(path).unwrap_or_else(|_| panic!("Cannot read {path}"));
let mut chars = content.chars().peekable();
parse_value(&mut chars)
}
fn skip_ws(chars: &mut std::iter::Peekable<std::str::Chars>) {
while let Some(&c) = chars.peek() {
if c.is_whitespace() {
chars.next();
} else {
break;
}
}
}
fn parse_value(chars: &mut std::iter::Peekable<std::str::Chars>) -> Value {
skip_ws(chars);
match chars.peek() {
Some('"') => Value::Str(parse_string(chars)),
Some('{') => parse_object(chars),
Some('[') => parse_array(chars),
Some('t') | Some('f') => parse_bool(chars),
Some('n') => {
for _ in 0..4 {
chars.next();
}
Value::Null
}
Some(c) if *c == '-' || c.is_ascii_digit() => parse_number(chars),
other => panic!("Unexpected char: {other:?}"),
}
}
fn parse_string(chars: &mut std::iter::Peekable<std::str::Chars>) -> String {
chars.next();
let mut s = String::new();
loop {
match chars.next() {
Some('"') => return s,
Some('\\') => match chars.next() {
Some('n') => s.push('\n'),
Some('t') => s.push('\t'),
Some('"') => s.push('"'),
Some('\\') => s.push('\\'),
Some('/') => s.push('/'),
Some('u') => {
let hex: String = chars.take(4).collect();
let cp = u32::from_str_radix(&hex, 16).unwrap();
if let Some(c) = char::from_u32(cp) {
s.push(c);
}
}
other => panic!("Unknown escape: {other:?}"),
},
Some(c) => s.push(c),
None => panic!("Unterminated string"),
}
}
}
fn parse_number(chars: &mut std::iter::Peekable<std::str::Chars>) -> Value {
let mut s = String::new();
while let Some(&c) = chars.peek() {
if c == '-' || c == '+' || c == '.' || c == 'e' || c == 'E' || c.is_ascii_digit() {
s.push(c);
chars.next();
} else {
break;
}
}
Value::Number(s.parse().unwrap())
}
fn parse_bool(chars: &mut std::iter::Peekable<std::str::Chars>) -> Value {
if chars.peek() == Some(&'t') {
for _ in 0..4 {
chars.next();
}
Value::Bool(true)
} else {
for _ in 0..5 {
chars.next();
}
Value::Bool(false)
}
}
fn parse_object(chars: &mut std::iter::Peekable<std::str::Chars>) -> Value {
chars.next();
let mut map = HashMap::new();
skip_ws(chars);
if chars.peek() == Some(&'}') {
chars.next();
return Value::Object(map);
}
loop {
skip_ws(chars);
let key = parse_string(chars);
skip_ws(chars);
chars.next();
let val = parse_value(chars);
map.insert(key, val);
skip_ws(chars);
match chars.peek() {
Some(',') => {
chars.next();
}
Some('}') => {
chars.next();
return Value::Object(map);
}
other => panic!("Expected , or }} got {other:?}"),
}
}
}
fn parse_array(chars: &mut std::iter::Peekable<std::str::Chars>) -> Value {
chars.next();
let mut arr = Vec::new();
skip_ws(chars);
if chars.peek() == Some(&']') {
chars.next();
return Value::Array(arr);
}
loop {
arr.push(parse_value(chars));
skip_ws(chars);
match chars.peek() {
Some(',') => {
chars.next();
}
Some(']') => {
chars.next();
return Value::Array(arr);
}
other => panic!("Expected , or ] got {other:?}"),
}
}
}
pub(crate) fn parse_rsp(path: &str) -> Vec<HashMap<String, String>> {
let content = std::fs::read_to_string(path).unwrap_or_else(|_| panic!("Cannot read {path}"));
let mut results = Vec::new();
let mut current: HashMap<String, String> = HashMap::new();
for line in content.lines() {
let line = line.trim();
if line.is_empty() || line.starts_with('#') {
if !current.is_empty() {
results.push(current.clone());
current.clear();
}
continue;
}
if let Some(pos) = line.find('=') {
let key = line[..pos].trim().to_string();
let val = line[pos + 1..].trim().to_string();
if key == "count" && !current.is_empty() {
results.push(current.clone());
current.clear();
}
current.insert(key, val);
}
}
if !current.is_empty() {
results.push(current);
}
results
}