use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use serde_json::Value;
use crate::channel::hello::PROTOCOL;
pub(crate) const CLASSES: [&str; 3] = ["answers", "refusals", "unreadable"];
const DIRECTION: &str = "direction";
pub(crate) fn root() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("corpus")
}
pub(crate) fn files(dir: &str) -> Vec<PathBuf> {
let mut found: Vec<PathBuf> = std::fs::read_dir(root().join(dir))
.unwrap_or_else(|e| panic!("corpus/{dir}: {e}"))
.flatten()
.map(|entry| entry.path())
.filter(|path| path.extension().is_some_and(|ext| ext == "json"))
.collect();
found.sort();
assert!(
!found.is_empty(),
"corpus/{dir} holds no file — the walk is broken, not the corpus"
);
found
}
pub(crate) struct Fixture {
pub(crate) path: PathBuf,
pub(crate) shape: Option<String>,
pub(crate) frames: Vec<Value>,
}
pub(crate) fn fixture(path: &Path) -> Fixture {
let text = std::fs::read_to_string(path).expect("a corpus file");
let value: Value = serde_json::from_str(&text)
.unwrap_or_else(|e| panic!("{}: not JSON — {e}", path.display()));
let Some(obj) = value.as_object().filter(|o| o.contains_key(DIRECTION)) else {
return Fixture {
path: path.to_owned(),
shape: None,
frames: vec![value],
};
};
let stamp = obj["protocol"].as_u64().expect("a stamped fixture");
assert!(
stamp <= u64::from(PROTOCOL),
"{} is stamped protocol {stamp} and this seat speaks {PROTOCOL} — \
the corpus is ahead of this build; upgrade the seat",
path.display()
);
let frames = obj["frames"].as_array().expect("frames").clone();
assert!(
!frames.is_empty(),
"{} carries no frame — a fixture that replays nothing passes forever",
path.display()
);
Fixture {
path: path.to_owned(),
shape: Some(obj["shape"].as_str().expect("a named shape").to_owned()),
frames,
}
}
pub(crate) struct Record {
pub(crate) protocol: u64,
pub(crate) shapes: BTreeMap<String, Vec<String>>,
}
pub(crate) fn record() -> Record {
let text = std::fs::read_to_string(root().join("shapes.json")).expect("corpus/shapes.json");
let value: Value = serde_json::from_str(&text).expect("shapes.json is JSON");
Record {
protocol: value["protocol"].as_u64().expect("a corpus protocol"),
shapes: value["shapes"]
.as_object()
.expect("the shape record")
.iter()
.map(|(key, shape)| {
let signature = shape["signature"]
.as_array()
.expect("a signature")
.iter()
.map(|field| field.as_str().expect("a field").to_owned())
.collect();
(key.clone(), signature)
})
.collect(),
}
}