use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
#[non_exhaustive]
pub enum Severity {
Hint,
Warn,
Block,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Signal {
pub severity: Severity,
pub origin: String,
pub message: String,
pub agent_hint: Option<String>,
pub auto_fix: Option<FixPatch>,
pub location: Option<CodeSpan>,
}
impl Signal {
pub fn is_blocking(&self) -> bool {
matches!(self.severity, Severity::Block)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CodeSpan {
pub path: PathBuf,
pub line: u32,
pub column: u32,
pub length: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub enum FixPatch {
ReplaceFile { path: PathBuf, content: String },
UnifiedDiff { diff: String },
RunCommand {
program: String,
args: Vec<String>,
cwd: Option<PathBuf>,
},
}
#[derive(Debug, Default)]
pub struct SignalSet {
pub signals: Vec<Signal>,
}
impl SignalSet {
pub fn new(signals: Vec<Signal>) -> Self {
Self { signals }
}
pub fn has_blocking(&self) -> bool {
self.signals.iter().any(Signal::is_blocking)
}
pub fn partition_auto_fix(self) -> (Vec<FixPatch>, SignalSet) {
let mut patches = Vec::new();
let mut remaining = Vec::new();
for s in self.signals {
if let Some(p) = s.auto_fix.clone() {
patches.push(p);
} else {
remaining.push(s);
}
}
(patches, SignalSet { signals: remaining })
}
pub fn is_clean(&self) -> bool {
self.signals.is_empty()
}
}