powerio_pkg/
diagnostics.rs1use serde::{Deserialize, Serialize};
11
12use crate::provenance::SourceRef;
13
14#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
20#[serde(transparent)]
21pub struct DiagnosticCode(pub String);
22
23impl DiagnosticCode {
24 pub fn new(code: impl Into<String>) -> Self {
25 Self(code.into())
26 }
27
28 pub fn namespace(&self) -> &str {
31 self.0.split('.').next().unwrap_or("")
32 }
33
34 pub fn as_str(&self) -> &str {
35 &self.0
36 }
37}
38
39impl std::fmt::Display for DiagnosticCode {
40 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41 f.write_str(&self.0)
42 }
43}
44
45impl From<&str> for DiagnosticCode {
46 fn from(s: &str) -> Self {
47 Self(s.to_owned())
48 }
49}
50
51impl From<String> for DiagnosticCode {
52 fn from(s: String) -> Self {
53 Self(s)
54 }
55}
56
57#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
59#[serde(rename_all = "snake_case")]
60pub enum DiagnosticSeverity {
61 Debug,
63 Info,
65 Warning,
68 Error,
71 Fatal,
73}
74
75#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
77#[serde(rename_all = "snake_case")]
78#[non_exhaustive]
79pub enum DiagnosticStage {
80 Parse,
81 Read,
82 Canonicalize,
83 Validate,
84 Lower,
85 Emit,
86 Bind,
87 Partner,
88}
89
90#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
92pub struct StructuredDiagnostic {
93 pub code: DiagnosticCode,
94 pub severity: DiagnosticSeverity,
95 pub stage: DiagnosticStage,
96 pub message: String,
97 #[serde(default, skip_serializing_if = "Option::is_none")]
99 pub element_path: Option<String>,
100 #[serde(default, skip_serializing_if = "Option::is_none")]
101 pub source_ref: Option<SourceRef>,
102 #[serde(default, skip_serializing_if = "serde_json::Map::is_empty")]
104 pub details: serde_json::Map<String, serde_json::Value>,
105 #[serde(default, skip_serializing_if = "Option::is_none")]
106 pub suggested_action: Option<String>,
107 #[serde(default, skip_serializing_if = "Vec::is_empty")]
110 pub safe_to_ignore: Vec<String>,
111}
112
113impl StructuredDiagnostic {
114 pub fn new(
116 code: impl Into<DiagnosticCode>,
117 severity: DiagnosticSeverity,
118 stage: DiagnosticStage,
119 message: impl Into<String>,
120 ) -> Self {
121 Self {
122 code: code.into(),
123 severity,
124 stage,
125 message: message.into(),
126 element_path: None,
127 source_ref: None,
128 details: serde_json::Map::new(),
129 suggested_action: None,
130 safe_to_ignore: Vec::new(),
131 }
132 }
133
134 #[must_use]
135 pub fn with_element_path(mut self, path: impl Into<String>) -> Self {
136 self.element_path = Some(path.into());
137 self
138 }
139
140 #[must_use]
141 pub fn with_source_ref(mut self, source_ref: SourceRef) -> Self {
142 self.source_ref = Some(source_ref);
143 self
144 }
145
146 #[must_use]
147 pub fn with_suggested_action(mut self, action: impl Into<String>) -> Self {
148 self.suggested_action = Some(action.into());
149 self
150 }
151}