use vil_ir::core::WorkflowIR;
use vil_types::{LaneKind, QueueKind, ReactiveInterfaceKind};
use crate::traits::{Diagnostic, ValidationPass, ValidationReport};
pub struct LaneLegalityPass;
impl ValidationPass for LaneLegalityPass {
fn name(&self) -> &'static str {
"LaneLegalityPass"
}
fn run(&self, ir: &WorkflowIR) -> ValidationReport {
let mut report = ValidationReport::new();
for (iface_name, iface) in &ir.interfaces {
for (port_name, port) in &iface.ports {
let context = format!("interface '{}', port '{}'", iface_name, port_name);
if port.lane_kind == LaneKind::Trigger && port.queue_spec.kind == QueueKind::Spsc {
report.push(Diagnostic::warning(
"W-LANE-01",
"Trigger lane using SPSC queue. External handoffs (e.g. from web servers) often involve multiple threads. MPMC is safer to avoid congestion.",
&context,
));
}
if port.lane_kind == LaneKind::Control {
if port.message_name != "ControlSignal" && port.message_name != "GenericToken" {
report.push(Diagnostic::warning(
"W-LANE-02",
format!(
"Control lane uses heavy payload '{}'. Consider using 'ControlSignal' for out-of-band signals.",
port.message_name
),
&context,
));
}
}
}
}
report
}
}
pub struct ReactiveInterfacePass;
impl ValidationPass for ReactiveInterfacePass {
fn name(&self) -> &'static str {
"ReactiveInterfacePass"
}
fn run(&self, ir: &WorkflowIR) -> ValidationReport {
let mut report = ValidationReport::new();
for (iface_name, iface) in &ir.interfaces {
let context = format!("interface '{}'", iface_name);
if iface.reactive_kind == ReactiveInterfaceKind::SessionReactive {
let has_control = iface
.ports
.values()
.any(|p| p.lane_kind == LaneKind::Control);
if !has_control {
report.push(Diagnostic::error(
"E-REACTIVE-01",
"SessionReactive interface must have at least one port with LaneKind::Control to ensure deterministic session termination.",
&context,
));
}
let has_data = iface
.ports
.values()
.any(|p| p.lane_kind == LaneKind::Data || p.lane_kind == LaneKind::Trigger);
if !has_data {
report.push(Diagnostic::error(
"E-REACTIVE-02",
"SessionReactive interface missing Data/Trigger lane. A session fabric requires data payload delivery.",
&context,
));
}
}
}
report
}
}