use std::{error::Error, fmt::Display, path::PathBuf};
use serde::{Deserialize, Serialize};
#[derive(Deserialize, Serialize, Clone)]
pub(crate) struct Document {
pub(crate) input: Input,
pub(crate) queries: Vec<Query>,
}
#[derive(Deserialize, Serialize, Clone)]
pub(crate) struct Input {
pub(crate) description: String,
pub(crate) is_compressed: bool,
pub(crate) source: InputSource,
}
#[derive(Deserialize, Serialize, Clone)]
pub(crate) enum InputSource {
#[serde(rename = "large_file")]
LargeFile(PathBuf),
#[serde(rename = "json_string")]
JsonString(String),
}
#[derive(Deserialize, Serialize, Clone)]
pub(crate) struct Query {
pub(crate) description: String,
pub(crate) query: String,
pub(crate) results: Results,
}
#[derive(Deserialize, Serialize, Clone)]
pub(crate) struct Results {
pub(crate) count: u64,
pub(crate) bytes: Option<Vec<usize>>,
pub(crate) nodes: Option<Vec<String>>,
}
pub(crate) fn serialize(doc: &Document) -> String {
toml::to_string(doc).expect("generated toml must be valid")
}
pub(crate) fn deserialize<S: AsRef<str>>(contents: S) -> Result<Document, ConfigurationError> {
let doc = toml::from_str(contents.as_ref()).map_err(ConfigurationError::DeserializationError)?;
validate(&doc).map_err(ConfigurationError::ValidationError)?;
Ok(doc)
}
#[derive(Debug)]
pub(crate) enum ConfigurationError {
DeserializationError(toml::de::Error),
ValidationError(ValidationError),
}
#[derive(Debug)]
pub(crate) struct ValidationError {
msg: String,
}
impl Display for ConfigurationError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::DeserializationError(err) => write!(f, "{err}"),
Self::ValidationError(err) => write!(f, "{err}"),
}
}
}
impl Display for ValidationError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.msg)
}
}
impl Error for ConfigurationError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::DeserializationError(err) => Some(err),
Self::ValidationError(err) => Some(err),
}
}
}
impl Error for ValidationError {}
fn validate(doc: &Document) -> Result<(), ValidationError> {
if doc.queries.is_empty() {
return err(&"no queries defined for the doc");
}
for query in &doc.queries {
if query.results.count <= 64 && query.results.bytes.is_none() {
return err(&format!(
"query {} with a small result count does not define expected bytes result; \
such tests are considered weak and should be updated to expect specific \
parts of the JSON to be matched",
query.query
));
}
}
Ok(())
}
fn err<S: ToString>(msg: &S) -> Result<(), ValidationError> {
Err(ValidationError { msg: msg.to_string() })
}