use std::fmt::Display;
use TestResultStatus::*;
use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ValidationError {
pub message: String,
pub instance_path: String,
}
impl std::fmt::Display for ValidationError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "ValidationError: {} at {}", self.message, self.instance_path)
}
}
pub trait IntoValidationError {
fn into_validation_error(self, instance_path: &str) -> ValidationError;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TestResult {
pub test_id: String,
pub status: TestResultStatus,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum TestResultStatus {
Success,
Failure {
errors: Vec<ValidationError>,
warnings: Vec<ValidationError>,
infos: Vec<ValidationError>,
},
NotFound,
Skipped,
}
impl Display for TestResultStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Success => write!(f, "Success"),
Failure {
errors,
warnings,
infos,
} => write!(
f,
"Failure with {} error(s), {} warning(s) and {} info(s)",
errors.len(),
warnings.len(),
infos.len()
),
NotFound => write!(f, "Test not found"),
Skipped => write!(f, "Test skipped"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ValidationResult {
pub success: bool,
pub version: String,
pub test_results: Vec<TestResult>,
pub num_errors: usize,
pub num_warnings: usize,
pub num_infos: usize,
pub num_not_found: usize,
}
pub trait Validate {
fn validate_by_test(&self, test_id: &str) -> TestResult;
fn validate_by_tests(&self, version: &str, test_ids: &[&str]) -> ValidationResult;
fn validate_by_preset(&self, version: &str, preset: &str) -> ValidationResult;
}
pub trait Validatable {
fn tests_in_preset(preset: &str) -> Option<Vec<&'static str>>;
fn run_test(&self, test_id: &str) -> TestResult;
}
pub fn validate_by_test(target: &impl Validatable, test_id: &str) -> TestResult {
target.run_test(test_id)
}
pub fn validate_by_tests(target: &impl Validatable, version: &str, test_ids: &[&str]) -> ValidationResult {
let mut test_results = Vec::new();
let mut num_errors: usize = 0;
let mut num_warnings: usize = 0;
let mut num_infos: usize = 0;
let mut num_not_found: usize = 0;
for test_id in test_ids {
let test_result = validate_by_test(target, test_id);
match &test_result.status {
Failure {
errors,
warnings,
infos,
} => {
num_errors += errors.len();
num_warnings += warnings.len();
num_infos += infos.len();
},
NotFound => {
num_not_found += 1;
},
_ => {},
}
test_results.push(test_result);
}
ValidationResult {
success: num_errors == 0,
version: version.to_string(),
num_errors,
num_warnings,
num_infos,
num_not_found,
test_results,
}
}
pub fn validate_by_preset<V: Validatable>(target: &V, version: &str, preset: &str) -> ValidationResult {
let test_ids: Vec<&str> = V::tests_in_preset(preset).unwrap_or(vec![]);
validate_by_tests(target, version, &test_ids)
}