use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use serde_json::{Value, json};
mod ledger;
mod store;
mod tests;
const REQUEST: &str = "request";
const REPLY: &str = "reply";
const DESTINATION: &str = "YOG_CORPUS_OUT";
pub(crate) struct Shape {
direction: &'static str,
name: String,
frames: Vec<Value>,
}
impl Shape {
pub(crate) fn key(&self) -> String {
format!("{}/{}", self.direction, self.name)
}
fn path(&self) -> String {
format!("{}/{}.json", self.direction, self.name)
}
fn render(&self, protocol: u32) -> String {
canonical(&json!({
"protocol": protocol,
"direction": self.direction,
"shape": self.name,
"frames": self.frames,
}))
}
}
fn canonical(value: &Value) -> String {
format!(
"{}\n",
serde_json::to_string_pretty(value).unwrap_or_default()
)
}
fn protocol() -> u32 {
crate::wire::hello::PROTOCOL
}
pub(crate) fn shapes() -> Vec<Shape> {
let requests = crate::boundary::codec::tests::surface::gestures()
.iter()
.map(crate::boundary::codec::encode)
.collect();
let mut replies: Vec<Value> = crate::boundary::reply::tests::roundtrip::surface::surface()
.iter()
.map(crate::boundary::reply::encode)
.collect();
replies.push(crate::boundary::reply::refusal("unknown op \"fhtagn\""));
let mut out = grouped(REQUEST, requests, op_of);
out.extend(grouped(REPLY, replies, kind_of));
out
}
fn op_of(frame: &Value) -> String {
frame
.get("op")
.and_then(Value::as_str)
.unwrap_or("unspelled")
.to_owned()
}
fn kind_of(frame: &Value) -> String {
frame
.get("kind")
.and_then(Value::as_str)
.unwrap_or("refusal")
.to_owned()
}
fn grouped(direction: &'static str, frames: Vec<Value>, key: fn(&Value) -> String) -> Vec<Shape> {
let mut buckets: BTreeMap<String, Vec<Value>> = BTreeMap::new();
for frame in frames {
let bucket = buckets.entry(key(&frame)).or_default();
if !bucket.contains(&frame) {
bucket.push(frame);
}
}
buckets
.into_iter()
.map(|(name, frames)| Shape {
direction,
name,
frames,
})
.collect()
}
fn committed() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("corpus")
}
fn destination() -> Option<PathBuf> {
std::env::var_os(DESTINATION).map(PathBuf::from)
}
fn run(destination: Option<PathBuf>, committed: &Path) -> Result<(), String> {
match destination {
Some(dir) => store::bless(&dir),
None => store::check(committed),
}
}