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