use std::fmt;
use serde::Serialize;
use super::matching::Expectation;
use crate::history::HistoryDiagnostic;
use crate::report::{Code, Diagnostic, Severity, SourceInfo};
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(untagged)]
pub enum Reported {
Contract(Diagnostic),
History(HistoryDiagnostic),
}
impl Reported {
#[must_use]
pub fn code(&self) -> Code {
match self {
Self::Contract(diagnostic) => diagnostic.code,
Self::History(diagnostic) => diagnostic.code,
}
}
#[must_use]
pub fn severity(&self) -> Severity {
match self {
Self::Contract(diagnostic) => diagnostic.severity,
Self::History(diagnostic) => diagnostic.severity,
}
}
#[must_use]
pub fn line(&self) -> Option<u32> {
match self {
Self::Contract(diagnostic) => diagnostic.line,
Self::History(diagnostic) => diagnostic.line,
}
}
#[must_use]
pub fn path(&self) -> Option<&str> {
match self {
Self::Contract(diagnostic) => Some(&diagnostic.path),
Self::History(diagnostic) => match &diagnostic.target {
crate::history::Target::Path { path } => Some(path),
_ => None,
},
}
}
#[must_use]
pub fn commit(&self) -> Option<&str> {
match self {
Self::Contract(_) => None,
Self::History(diagnostic) => match &diagnostic.target {
crate::history::Target::Commit { commit } => Some(commit),
_ => None,
},
}
}
#[must_use]
pub fn rule(&self) -> Option<&str> {
match self {
Self::Contract(diagnostic) => diagnostic.rule.as_deref(),
Self::History(diagnostic) => diagnostic.rule.as_deref(),
}
}
#[must_use]
pub fn message(&self) -> &str {
match self {
Self::Contract(diagnostic) => &diagnostic.message,
Self::History(diagnostic) => &diagnostic.message,
}
}
}
impl fmt::Display for Reported {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Contract(diagnostic) => diagnostic.fmt(f),
Self::History(diagnostic) => diagnostic.fmt(f),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum Outcome {
Clean,
Diagnostics,
Fatal,
}
impl fmt::Display for Outcome {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::Clean => "clean",
Self::Diagnostics => "diagnostics",
Self::Fatal => "fatal",
})
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum Matching {
#[default]
Exact,
Contains,
}
#[derive(Debug, Clone, Serialize)]
pub struct CaseResult {
pub name: String,
pub file: String,
pub passed: bool,
pub expected: Outcome,
pub actual: Outcome,
pub missing: Vec<Expectation>,
pub unexpected: Vec<Reported>,
#[serde(skip_serializing_if = "Option::is_none")]
pub expected_fatal: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fatal: Option<String>,
}
#[derive(Debug, Default, Serialize)]
pub struct TestReport {
pub ok: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub source: Option<SourceInfo>,
pub total: usize,
pub passed: usize,
pub failed: usize,
pub cases: Vec<CaseResult>,
pub fatal: Option<String>,
}
impl TestReport {
#[must_use]
pub fn fatal(message: impl Into<String>) -> Self {
Self {
ok: false,
fatal: Some(message.into()),
..Self::default()
}
}
pub(crate) fn finish(&mut self) {
self.total = self.cases.len();
self.passed = self.cases.iter().filter(|case| case.passed).count();
self.failed = self.total - self.passed;
self.ok = self.fatal.is_none() && self.failed == 0;
}
}