use std::fmt;
use vil_ir::core::WorkflowIR;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Severity {
Error,
Warning,
Note,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Diagnostic {
pub severity: Severity,
pub code: String,
pub message: String,
pub context: String,
}
impl Diagnostic {
pub fn error(
code: impl Into<String>,
message: impl Into<String>,
context: impl Into<String>,
) -> Self {
Self {
severity: Severity::Error,
code: code.into(),
message: message.into(),
context: context.into(),
}
}
pub fn warning(
code: impl Into<String>,
message: impl Into<String>,
context: impl Into<String>,
) -> Self {
Self {
severity: Severity::Warning,
code: code.into(),
message: message.into(),
context: context.into(),
}
}
}
impl fmt::Display for Diagnostic {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let prefix = match self.severity {
Severity::Error => "error",
Severity::Warning => "warning",
Severity::Note => "note",
};
write!(
f,
"{}[{}]: {} (at {})",
prefix, self.code, self.message, self.context
)
}
}
#[derive(Debug, Default)]
pub struct ValidationReport {
pub diagnostics: Vec<Diagnostic>,
}
impl ValidationReport {
pub fn new() -> Self {
Self::default()
}
pub fn push(&mut self, diag: Diagnostic) {
self.diagnostics.push(diag);
}
pub fn has_errors(&self) -> bool {
self.diagnostics
.iter()
.any(|d| d.severity == Severity::Error)
}
pub fn merge(&mut self, mut other: ValidationReport) {
self.diagnostics.append(&mut other.diagnostics);
}
}
pub trait ValidationPass {
fn name(&self) -> &'static str;
fn run(&self, ir: &WorkflowIR) -> ValidationReport;
}