use perspt_sdk::{
EvidencePayload, ResidualClass, ResidualEvent, ResidualSeverity, SensorRef,
StructuredDiagnosticRef,
};
#[derive(Debug, Clone, PartialEq)]
pub struct StructuredDiagnostic {
pub class: ResidualClass,
pub code: Option<String>,
pub message: String,
pub path: Option<String>,
pub line: Option<u32>,
pub column: Option<u32>,
pub primary: bool,
pub suggestion: Option<String>,
}
impl StructuredDiagnostic {
pub fn to_ref(&self) -> StructuredDiagnosticRef {
StructuredDiagnosticRef {
code: self.code.clone(),
message: self.message.clone(),
path: self.path.clone(),
line: self.line,
column: self.column,
}
}
}
#[allow(clippy::too_many_arguments)]
pub fn cluster_residual(
node_id: &str,
generation: u32,
class: ResidualClass,
magnitude: f64,
sensor: SensorRef,
summary: &str,
raw: &str,
members: &[StructuredDiagnostic],
) -> anyhow::Result<ResidualEvent> {
let mut event = ResidualEvent::new(
node_id,
generation,
class,
ResidualSeverity::Error,
magnitude,
sensor,
)
.map_err(|e| anyhow::anyhow!("{e}"))?;
event.evidence = EvidencePayload {
summary: summary.to_string(),
raw: Some(bounded(raw, 16 * 1024)),
structured: Some(serde_json::to_value(
members
.iter()
.map(StructuredDiagnostic::to_ref)
.collect::<Vec<_>>(),
)?),
};
event.affected_paths = members
.iter()
.filter_map(|member| member.path.clone())
.collect::<std::collections::BTreeSet<_>>()
.into_iter()
.collect();
Ok(event)
}
fn bounded(raw: &str, cap: usize) -> String {
if raw.len() <= cap {
return raw.to_string();
}
let mut end = cap;
while !raw.is_char_boundary(end) {
end -= 1;
}
format!("{}\n[truncated {} bytes]", &raw[..end], raw.len() - end)
}