assay_core/errors/
diagnostic.rs1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Serialize, Deserialize)]
4pub struct Diagnostic {
5 pub code: String,
6 pub severity: String,
7 pub source: String,
8 pub message: String,
9 pub context: serde_json::Value,
10 pub fix_steps: Vec<String>,
11}
12
13impl Diagnostic {
14 pub fn new(code: impl Into<String>, message: impl Into<String>) -> Self {
15 Self {
16 code: code.into(),
17 severity: "error".into(), source: "unknown".into(),
19 message: message.into(),
20 context: serde_json::json!({}),
21 fix_steps: vec![],
22 }
23 }
24
25 pub fn with_severity(mut self, severity: impl Into<String>) -> Self {
26 self.severity = severity.into();
27 self
28 }
29
30 pub fn with_source(mut self, source: impl Into<String>) -> Self {
31 self.source = source.into();
32 self
33 }
34
35 pub fn with_context(mut self, context: serde_json::Value) -> Self {
36 self.context = context;
37 self
38 }
39
40 pub fn with_fix_step(mut self, step: impl Into<String>) -> Self {
41 self.fix_steps.push(step.into());
42 self
43 }
44
45 pub fn format_terminal(&self) -> String {
46 let icon = if self.severity == "warn" {
47 "⚠️ "
48 } else {
49 "❌"
50 };
51 let mut s = format!("{} [{}] {}\n", icon, self.code, self.message);
52 s.push_str(&format!(" source: {}\n", self.source));
53
54 if !self.context.is_null() && self.context.as_object().is_some_and(|o| !o.is_empty()) {
56 if let Ok(json) = serde_json::to_string_pretty(&self.context) {
57 for line in json.lines() {
59 s.push_str(&format!(" {}\n", line));
60 }
61 }
62 }
63
64 if !self.fix_steps.is_empty() {
65 s.push_str("\nFix:\n");
66 for (i, step) in self.fix_steps.iter().enumerate() {
67 s.push_str(&format!(" {}. {}\n", i + 1, step));
68 }
69 }
70 s
71 }
72
73 pub fn format_plain(&self) -> String {
74 self.format_terminal()
76 }
77}
78
79impl std::fmt::Display for Diagnostic {
80 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81 write!(f, "{}", self.format_terminal())
82 }
83}
84
85impl std::error::Error for Diagnostic {}
86
87pub mod codes {
89 pub const E_CFG_PARSE: &str = "E_CFG_PARSE";
91 pub const E_CFG_SCHEMA: &str = "E_CFG_SCHEMA";
92 pub const E_PATH_NOT_FOUND: &str = "E_PATH_NOT_FOUND";
93 pub const E_TRACE_MISS: &str = "E_TRACE_MISS";
94 pub const E_TRACE_INVALID: &str = "E_TRACE_INVALID";
95 pub const E_BASE_MISMATCH: &str = "E_BASE_MISMATCH";
96 pub const E_REPLAY_STRICT_MISSING: &str = "E_REPLAY_STRICT_MISSING";
97 pub const E_EMB_DIMS: &str = "E_EMB_DIMS";
98
99 pub const W_BASE_FINGERPRINT: &str = "W_BASE_FINGERPRINT";
101 pub const W_CACHE_CONFUSION: &str = "W_CACHE_CONFUSION";
102}