perspt_coding/diag/
types.rs1use perspt_sdk::{
9 EvidencePayload, ResidualClass, ResidualEvent, ResidualSeverity, SensorRef,
10 StructuredDiagnosticRef,
11};
12
13#[derive(Debug, Clone, PartialEq)]
15pub struct StructuredDiagnostic {
16 pub class: ResidualClass,
17 pub code: Option<String>,
18 pub message: String,
19 pub path: Option<String>,
20 pub line: Option<u32>,
21 pub column: Option<u32>,
22 pub primary: bool,
25 pub suggestion: Option<String>,
27}
28
29impl StructuredDiagnostic {
30 pub fn to_ref(&self) -> StructuredDiagnosticRef {
31 StructuredDiagnosticRef {
32 code: self.code.clone(),
33 message: self.message.clone(),
34 path: self.path.clone(),
35 line: self.line,
36 column: self.column,
37 }
38 }
39}
40
41#[allow(clippy::too_many_arguments)]
43pub fn cluster_residual(
44 node_id: &str,
45 generation: u32,
46 class: ResidualClass,
47 magnitude: f64,
48 sensor: SensorRef,
49 summary: &str,
50 raw: &str,
51 members: &[StructuredDiagnostic],
52) -> anyhow::Result<ResidualEvent> {
53 let mut event = ResidualEvent::new(
54 node_id,
55 generation,
56 class,
57 ResidualSeverity::Error,
58 magnitude,
59 sensor,
60 )
61 .map_err(|e| anyhow::anyhow!("{e}"))?;
62 event.evidence = EvidencePayload {
63 summary: summary.to_string(),
64 raw: Some(bounded(raw, 16 * 1024)),
65 structured: Some(serde_json::to_value(
66 members
67 .iter()
68 .map(StructuredDiagnostic::to_ref)
69 .collect::<Vec<_>>(),
70 )?),
71 };
72 event.affected_paths = members
73 .iter()
74 .filter_map(|member| member.path.clone())
75 .collect::<std::collections::BTreeSet<_>>()
76 .into_iter()
77 .collect();
78 Ok(event)
79}
80
81fn bounded(raw: &str, cap: usize) -> String {
82 if raw.len() <= cap {
83 return raw.to_string();
84 }
85 let mut end = cap;
86 while !raw.is_char_boundary(end) {
87 end -= 1;
88 }
89 format!("{}\n[truncated {} bytes]", &raw[..end], raw.len() - end)
90}