1#[cfg(feature = "with-serde")]
2use crate::encoding::Annotations;
3
4#[derive(Debug, Clone, PartialEq, Eq)]
7#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))]
8pub struct Warning {
9 pub message: String,
11
12 pub r#type: String,
14
15 pub code: u64,
17
18 #[cfg_attr(feature = "with-serde", serde(default))]
20 pub filename: Option<String>,
21
22 #[cfg_attr(feature = "with-serde", serde(default))]
24 pub hint: Option<String>,
25
26 #[cfg_attr(feature = "with-serde", serde(default))]
28 pub details: Option<String>,
29
30 #[cfg_attr(
32 feature = "with-serde",
33 serde(deserialize_with = "deserialize_usize_from_str", default)
34 )]
35 pub start: Option<usize>,
36
37 #[cfg_attr(
39 feature = "with-serde",
40 serde(deserialize_with = "deserialize_usize_from_str", default)
41 )]
42 pub end: Option<usize>,
43
44 #[cfg_attr(
46 feature = "with-serde",
47 serde(deserialize_with = "deserialize_usize_from_str", default)
48 )]
49 pub line: Option<usize>,
50
51 #[cfg_attr(
53 feature = "with-serde",
54 serde(deserialize_with = "deserialize_usize_from_str", default)
55 )]
56 pub col: Option<usize>,
57}
58
59impl std::fmt::Display for Warning {
60 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61 let Warning {
62 filename,
63 line,
64 col,
65 r#type,
66 message,
67 ..
68 } = self;
69 let filename = filename
70 .as_ref()
71 .map(|f| format!("{f}:"))
72 .unwrap_or_default();
73 let line = (*line).unwrap_or(1);
74 let col = (*col).unwrap_or(1);
75
76 write!(f, "{type} at {filename}{line}:{col} {message}")
77 }
78}
79
80#[cfg(feature = "with-serde")]
81pub fn decode_warnings(annotations: &Annotations) -> Result<Vec<Warning>, gel_errors::Error> {
82 use gel_errors::{ErrorKind, ProtocolEncodingError};
83
84 const ANN_NAME: &str = "warnings";
85
86 if let Some(warnings) = annotations.get(ANN_NAME) {
87 serde_json::from_str::<Vec<_>>(warnings).map_err(|e| {
88 ProtocolEncodingError::with_source(e)
89 .context("Invalid JSON while decoding 'warnings' annotation")
90 })
91 } else {
92 Ok(vec![])
93 }
94}
95
96#[cfg(feature = "with-serde")]
97fn deserialize_usize_from_str<'de, D: serde::Deserializer<'de>>(
98 deserializer: D,
99) -> Result<Option<usize>, D::Error> {
100 use serde::Deserialize;
101
102 #[derive(Deserialize)]
103 #[serde(untagged)]
104 enum StringOrInt {
105 String(String),
106 Number(usize),
107 }
108
109 Option::<StringOrInt>::deserialize(deserializer)?
110 .map(|x| match x {
111 StringOrInt::String(s) => s.parse::<usize>().map_err(serde::de::Error::custom),
112 StringOrInt::Number(i) => Ok(i),
113 })
114 .transpose()
115}
116
117#[test]
118#[cfg(feature = "with-serde")]
119fn deserialize_warning() {
120 let a: Warning =
121 serde_json::from_str(r#"{"message": "a", "type": "WarningException", "code": 1}"#).unwrap();
122 assert_eq!(
123 a,
124 Warning {
125 message: "a".to_string(),
126 r#type: "WarningException".to_string(),
127 code: 1,
128 filename: None,
129 hint: None,
130 details: None,
131 start: None,
132 end: None,
133 line: None,
134 col: None
135 }
136 );
137
138 let a: Warning = serde_json::from_str(
139 r#"{"message": "a", "type": "WarningException", "code": 1, "start": null}"#,
140 )
141 .unwrap();
142 assert_eq!(
143 a,
144 Warning {
145 message: "a".to_string(),
146 r#type: "WarningException".to_string(),
147 code: 1,
148 filename: None,
149 hint: None,
150 details: None,
151 start: None,
152 end: None,
153 line: None,
154 col: None
155 }
156 );
157
158 let a: Warning = serde_json::from_str(
159 r#"{"message": "a", "type": "WarningException", "code": 1, "start": 23}"#,
160 )
161 .unwrap();
162 assert_eq!(
163 a,
164 Warning {
165 message: "a".to_string(),
166 r#type: "WarningException".to_string(),
167 code: 1,
168 filename: None,
169 hint: None,
170 details: None,
171 start: Some(23),
172 end: None,
173 line: None,
174 col: None
175 }
176 );
177
178 let a: Warning = serde_json::from_str(
179 r#"{"message": "a", "type": "WarningException", "code": 1, "start": "23"}"#,
180 )
181 .unwrap();
182 assert_eq!(
183 a,
184 Warning {
185 message: "a".to_string(),
186 r#type: "WarningException".to_string(),
187 code: 1,
188 filename: None,
189 hint: None,
190 details: None,
191 start: Some(23),
192 end: None,
193 line: None,
194 col: None
195 }
196 );
197}