powerio_dist/
diagnostics.rs1use serde::{Deserialize, Serialize};
7
8#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
10#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
11#[serde(transparent)]
12pub struct DiagnosticCode(pub String);
13
14impl DiagnosticCode {
15 pub fn new(code: impl Into<String>) -> Self {
16 Self(code.into())
17 }
18
19 pub fn namespace(&self) -> &str {
20 self.0.split('.').next().unwrap_or("")
21 }
22
23 pub fn as_str(&self) -> &str {
24 &self.0
25 }
26}
27
28impl std::fmt::Display for DiagnosticCode {
29 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30 f.write_str(&self.0)
31 }
32}
33
34impl From<&str> for DiagnosticCode {
35 fn from(s: &str) -> Self {
36 Self(s.to_owned())
37 }
38}
39
40impl From<String> for DiagnosticCode {
41 fn from(s: String) -> Self {
42 Self(s)
43 }
44}
45
46#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
48#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
49#[serde(rename_all = "snake_case")]
50pub enum DiagnosticSeverity {
51 Debug,
52 Info,
53 Warning,
54 Error,
55 Fatal,
56}
57
58#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
60#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
61#[serde(rename_all = "snake_case")]
62#[non_exhaustive]
63pub enum DiagnosticStage {
64 Parse,
65 Read,
66 Canonicalize,
67 Validate,
68 Lower,
69 Emit,
70 Bind,
71 Partner,
72}
73
74pub const READ_DSS_INCLUDE_REFUSED: &str = "READ.DSS.INCLUDE_REFUSED";
78
79pub const READ_DSS_INCLUDE_BUDGET: &str = "READ.DSS.INCLUDE_BUDGET";
83
84pub const READ_BMOPF_FIELD_NOT_A_NUMBER: &str = "READ.BMOPF.FIELD_NOT_A_NUMBER";
88
89#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
91#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
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 = "serde_json::Map::is_empty")]
102 pub details: serde_json::Map<String, serde_json::Value>,
103 #[serde(default, skip_serializing_if = "Option::is_none")]
104 pub suggested_action: Option<String>,
105 #[serde(default, skip_serializing_if = "Vec::is_empty")]
107 pub safe_to_ignore: Vec<String>,
108}
109
110impl StructuredDiagnostic {
111 pub fn new(
112 code: impl Into<DiagnosticCode>,
113 severity: DiagnosticSeverity,
114 stage: DiagnosticStage,
115 message: impl Into<String>,
116 ) -> Self {
117 Self {
118 code: code.into(),
119 severity,
120 stage,
121 message: message.into(),
122 element_path: None,
123 details: serde_json::Map::new(),
124 suggested_action: None,
125 safe_to_ignore: Vec::new(),
126 }
127 }
128
129 #[must_use]
130 pub fn with_element_path(mut self, path: impl Into<String>) -> Self {
131 self.element_path = Some(path.into());
132 self
133 }
134
135 #[must_use]
136 pub fn with_details(mut self, details: serde_json::Map<String, serde_json::Value>) -> Self {
137 self.details = details;
138 self
139 }
140
141 #[must_use]
142 pub fn with_suggested_action(mut self, action: impl Into<String>) -> Self {
143 self.suggested_action = Some(action.into());
144 self
145 }
146}