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_BMOPF_FIELD_NOT_A_NUMBER: &str = "READ.BMOPF.FIELD_NOT_A_NUMBER";
83
84#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
86#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
87pub struct StructuredDiagnostic {
88 pub code: DiagnosticCode,
89 pub severity: DiagnosticSeverity,
90 pub stage: DiagnosticStage,
91 pub message: String,
92 #[serde(default, skip_serializing_if = "Option::is_none")]
94 pub element_path: Option<String>,
95 #[serde(default, skip_serializing_if = "serde_json::Map::is_empty")]
97 pub details: serde_json::Map<String, serde_json::Value>,
98 #[serde(default, skip_serializing_if = "Option::is_none")]
99 pub suggested_action: Option<String>,
100 #[serde(default, skip_serializing_if = "Vec::is_empty")]
102 pub safe_to_ignore: Vec<String>,
103}
104
105impl StructuredDiagnostic {
106 pub fn new(
107 code: impl Into<DiagnosticCode>,
108 severity: DiagnosticSeverity,
109 stage: DiagnosticStage,
110 message: impl Into<String>,
111 ) -> Self {
112 Self {
113 code: code.into(),
114 severity,
115 stage,
116 message: message.into(),
117 element_path: None,
118 details: serde_json::Map::new(),
119 suggested_action: None,
120 safe_to_ignore: Vec::new(),
121 }
122 }
123
124 #[must_use]
125 pub fn with_element_path(mut self, path: impl Into<String>) -> Self {
126 self.element_path = Some(path.into());
127 self
128 }
129
130 #[must_use]
131 pub fn with_details(mut self, details: serde_json::Map<String, serde_json::Value>) -> Self {
132 self.details = details;
133 self
134 }
135
136 #[must_use]
137 pub fn with_suggested_action(mut self, action: impl Into<String>) -> Self {
138 self.suggested_action = Some(action.into());
139 self
140 }
141}