use crate::{Scale, Section, Test};
use anyhow::Result;
use serde_json::Value;
use std::{
fs,
path::{Path, PathBuf},
};
pub struct Entry {
pub section: Section,
scale: Option<Scale>,
files: Vec<PathBuf>,
current: usize,
}
impl Entry {
pub fn new(section: Section, scale: Option<Scale>, stf: &Path) -> Result<Self> {
let dir = {
if let Some(scale) = scale {
stf.join(section.as_ref()).join(scale.as_ref())
} else {
stf.join(section.as_ref())
}
};
if !dir.exists() {
return Err(anyhow::anyhow!("directory does not exist"));
}
let mut files = Vec::new();
for entry in fs::read_dir(dir)? {
let path = entry?.path();
if path.is_file() && path.extension().unwrap_or_default() == "json" {
files.push(path);
}
}
Ok(Self {
section,
scale,
files,
current: 0,
})
}
pub fn count(&self) -> usize {
self.files.len()
}
pub fn get(&self, index: usize) -> Result<Test> {
let path = self
.files
.get(index)
.ok_or_else(|| anyhow::anyhow!("index out of bounds"))?;
self.parse(path)
}
pub fn test(&self, name: &str) -> Result<Test> {
let path = self
.files
.iter()
.find(|path| {
let path = path.with_extension("");
path.file_name()
.unwrap_or_default()
.to_str()
.unwrap_or_default()
== name
})
.ok_or_else(|| anyhow::anyhow!("test not found"))?;
self.parse(path)
}
pub fn parse(&self, path: &PathBuf) -> Result<Test> {
match self.section {
Section::Accumulate => self.parse_general(path),
Section::Assurances => self.parse_general(path),
Section::Authorizations => self.parse_general(path),
Section::Codec => self.parse_codec(path),
Section::Pvm => self.parse_pvm(path),
Section::Shuffle => self.parse_shuffle(path),
Section::Trie => self.parse_trie(path),
Section::Trace(_) => self.parse_trace(path),
Section::Reports => self.parse_general(path),
Section::Statistics => self.parse_general(path),
Section::Safrole => self.parse_general(path),
Section::Disputes => self.parse_general(path),
Section::History => self.parse_general(path),
Section::Preimages => self.parse_general(path),
}
}
fn parse_codec(&self, path: &PathBuf) -> Result<Test> {
let name = Self::file_name(path)?;
let input = fs::read_to_string(path)?;
let output = hex::encode(fs::read(path.with_extension("bin"))?);
Ok(Test {
input,
output,
name,
scale: self.scale,
section: self.section,
})
}
fn parse_general(&self, path: &PathBuf) -> Result<Test> {
let name = Self::file_name(path)?;
let json: Value = serde_json::from_slice(&fs::read(path)?)?;
let input = serde_json::json!({
"input": json["input"],
"pre_state": json["pre_state"],
})
.to_string();
let output = serde_json::json!({
"output": json["output"],
"post_state": json["post_state"],
})
.to_string();
Ok(Test {
input,
output,
name,
scale: self.scale,
section: self.section,
})
}
fn parse_pvm(&self, path: &PathBuf) -> Result<Test> {
let name = Self::file_name(path)?;
let json: Value = serde_json::from_slice(&fs::read(path)?)?;
let input = serde_json::json!({
"name": json["name"],
"initial-regs": json["pre-state"],
"initial-pc": json["initial-pc"],
"initial-regs": json["initial-regs"],
"initial-page-map": json["initial-page-map"],
"initial-memory": json["initial-memory"],
"initial-gas": json["initial-gas"],
"program": json["program"],
})
.to_string();
let output = serde_json::json!({
"expected-status": json["expected-status"],
"expected-regs": json["expected-regs"],
"expected-pc": json["expected-pc"],
"expected-memory": json["expected-memory"],
"expected-gas": json["expected-gas"],
})
.to_string();
Ok(Test {
input,
output,
scale: self.scale,
section: self.section,
name,
})
}
fn parse_trace(&self, path: &PathBuf) -> Result<Test> {
let name = Self::file_name(path)?;
let json: Value = serde_json::from_slice(&fs::read(path)?)?;
let input = serde_json::json!({
"block": json["block"],
"pre_state": json["pre_state"],
})
.to_string();
let output = serde_json::json!({
"post_state": json["post_state"],
})
.to_string();
Ok(Test {
input,
output,
scale: self.scale,
section: self.section,
name,
})
}
fn parse_trie(&self, path: &PathBuf) -> Result<Test> {
let name = Self::file_name(path)?;
let json: Value = serde_json::from_slice(&fs::read(path)?)?;
let vectors = json
.as_array()
.ok_or_else(|| anyhow::anyhow!("invalid trie test"))?;
let mut input = Vec::new();
let mut output = Vec::new();
for vector in vectors {
input.push(serde_json::json!({
"input": vector["input"],
}));
output.push(serde_json::json!({
"output": vector["output"],
}));
}
Ok(Test {
input: serde_json::to_string(&input)?,
output: serde_json::to_string(&output)?,
scale: self.scale,
section: self.section,
name,
})
}
fn parse_shuffle(&self, path: &PathBuf) -> Result<Test> {
let name = Self::file_name(path)?;
let json: Value = serde_json::from_slice(&fs::read(path)?)?;
let vectors = json
.as_array()
.ok_or_else(|| anyhow::anyhow!("invalid shuffle test"))?;
let mut input = Vec::new();
let mut output = Vec::new();
for vector in vectors {
input.push(serde_json::json!({
"input": vector["input"],
"entropy": vector["entropy"],
}));
output.push(serde_json::json!({
"output": vector["output"],
}));
}
Ok(Test {
input: serde_json::to_string(&input)?,
output: serde_json::to_string(&output)?,
scale: self.scale,
section: self.section,
name,
})
}
fn file_name(path: &Path) -> Result<String> {
Ok(path
.with_extension("")
.file_name()
.ok_or_else(|| anyhow::anyhow!("invalid file name"))?
.to_string_lossy()
.to_string()
.replace('-', "_"))
}
}
impl Iterator for Entry {
type Item = Test;
fn next(&mut self) -> Option<Self::Item> {
let path = self.files.get(self.current)?;
let test = self.parse(path).ok()?;
self.current += 1;
Some(test)
}
}