1use serde::{Deserialize, Serialize};
5
6use super::ast::WireNode;
7use super::range::Range;
8
9#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
11pub struct Diagnostic {
12 pub severity: DiagnosticSeverity,
13 pub message: String,
14 pub range: Range,
15 #[serde(skip_serializing_if = "Option::is_none")]
16 pub code: Option<String>,
17 #[serde(default, skip_serializing_if = "Vec::is_empty")]
18 pub related: Vec<RelatedDiagnostic>,
19}
20
21#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
24pub struct RelatedDiagnostic {
25 pub message: String,
26 pub range: Range,
27 #[serde(skip_serializing_if = "Option::is_none")]
28 pub uri: Option<String>,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
37#[serde(rename_all = "snake_case")]
38#[non_exhaustive]
39pub enum DiagnosticSeverity {
40 Error,
41 Warning,
42 Info,
43 Hint,
44}
45
46impl<'de> Deserialize<'de> for DiagnosticSeverity {
47 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
48 where
49 D: serde::Deserializer<'de>,
50 {
51 let s = String::deserialize(deserializer)?;
52 Ok(match s.as_str() {
53 "error" => Self::Error,
54 "warning" => Self::Warning,
55 "info" => Self::Info,
56 "hint" => Self::Hint,
57 _ => Self::Info,
58 })
59 }
60}
61
62#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
65#[serde(tag = "kind", rename_all = "snake_case")]
66pub enum RenderOut {
67 String { string: String },
69 WireAst { ast: WireNode },
71}
72
73#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
75pub struct Hover {
76 pub contents: String,
77 pub format: HoverFormat,
78 #[serde(skip_serializing_if = "Option::is_none")]
79 pub range: Option<Range>,
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
87#[serde(rename_all = "snake_case")]
88#[non_exhaustive]
89pub enum HoverFormat {
90 Plaintext,
91 Markdown,
92}
93
94impl<'de> Deserialize<'de> for HoverFormat {
95 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
96 where
97 D: serde::Deserializer<'de>,
98 {
99 let s = String::deserialize(deserializer)?;
100 Ok(match s.as_str() {
101 "plaintext" => Self::Plaintext,
102 "markdown" => Self::Markdown,
103 _ => Self::Plaintext,
104 })
105 }
106}
107
108#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
110pub struct Completion {
111 pub label: String,
112 #[serde(skip_serializing_if = "Option::is_none")]
113 pub detail: Option<String>,
114 #[serde(skip_serializing_if = "Option::is_none")]
115 pub doc: Option<String>,
116 pub insert: String,
117 pub kind: CompletionKind,
118}
119
120#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
125#[serde(rename_all = "snake_case")]
126#[non_exhaustive]
127pub enum CompletionKind {
128 Value,
129 Param,
130 Namespace,
131 Snippet,
132}
133
134impl<'de> Deserialize<'de> for CompletionKind {
135 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
136 where
137 D: serde::Deserializer<'de>,
138 {
139 let s = String::deserialize(deserializer)?;
140 Ok(match s.as_str() {
141 "value" => Self::Value,
142 "param" => Self::Param,
143 "namespace" => Self::Namespace,
144 "snippet" => Self::Snippet,
145 _ => Self::Value,
146 })
147 }
148}
149
150#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
152pub struct CodeAction {
153 pub title: String,
154 pub kind: CodeActionKind,
155 #[serde(default, skip_serializing_if = "Vec::is_empty")]
156 pub edits: Vec<TextEdit>,
157}
158
159#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
164#[serde(rename_all = "snake_case")]
165#[non_exhaustive]
166pub enum CodeActionKind {
167 Quickfix,
168 Refactor,
169 Source,
170}
171
172impl<'de> Deserialize<'de> for CodeActionKind {
173 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
174 where
175 D: serde::Deserializer<'de>,
176 {
177 let s = String::deserialize(deserializer)?;
178 Ok(match s.as_str() {
179 "quickfix" => Self::Quickfix,
180 "refactor" => Self::Refactor,
181 "source" => Self::Source,
182 _ => Self::Refactor,
183 })
184 }
185}
186
187#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
189pub struct TextEdit {
190 pub range: Range,
191 pub new_text: String,
192 #[serde(skip_serializing_if = "Option::is_none")]
193 pub uri: Option<String>,
194}
195
196#[cfg(test)]
197mod tests {
198 use super::*;
199 use crate::wire::range::Position;
200
201 fn r(s_l: u32, s_c: u32, e_l: u32, e_c: u32) -> Range {
202 Range::new(Position::new(s_l, s_c), Position::new(e_l, e_c))
203 }
204
205 #[test]
206 fn diagnostic_round_trips() {
207 let d = Diagnostic {
208 severity: DiagnosticSeverity::Error,
209 message: "oops".into(),
210 range: r(0, 0, 0, 5),
211 code: Some("E001".into()),
212 related: vec![],
213 };
214 let s = serde_json::to_string(&d).unwrap();
215 let back: Diagnostic = serde_json::from_str(&s).unwrap();
216 assert_eq!(back, d);
217 }
218
219 #[test]
220 fn render_out_string_round_trips() {
221 let r0 = RenderOut::String {
222 string: "<p>hi</p>".into(),
223 };
224 let s = serde_json::to_string(&r0).unwrap();
225 assert!(s.contains(r#""kind":"string""#));
226 let back: RenderOut = serde_json::from_str(&s).unwrap();
227 assert_eq!(back, r0);
228 }
229
230 #[test]
231 fn render_out_wire_ast_round_trips() {
232 let r0 = RenderOut::WireAst {
233 ast: WireNode::Paragraph {
234 range: r(0, 0, 0, 5),
235 origin: None,
236 inlines: vec![],
237 },
238 };
239 let s = serde_json::to_string(&r0).unwrap();
240 assert!(s.contains(r#""kind":"wire_ast""#));
241 let back: RenderOut = serde_json::from_str(&s).unwrap();
242 assert_eq!(back, r0);
243 }
244
245 #[test]
246 fn hover_round_trips() {
247 let h = Hover {
248 contents: "**bold**".into(),
249 format: HoverFormat::Markdown,
250 range: Some(r(0, 0, 0, 5)),
251 };
252 let s = serde_json::to_string(&h).unwrap();
253 let back: Hover = serde_json::from_str(&s).unwrap();
254 assert_eq!(back, h);
255 }
256
257 #[test]
258 fn completion_round_trips() {
259 let c = Completion {
260 label: "foo".into(),
261 detail: Some("Foo the bar".into()),
262 doc: None,
263 insert: "foo".into(),
264 kind: CompletionKind::Param,
265 };
266 let s = serde_json::to_string(&c).unwrap();
267 let back: Completion = serde_json::from_str(&s).unwrap();
268 assert_eq!(back, c);
269 }
270
271 #[test]
272 fn code_action_round_trips() {
273 let a = CodeAction {
274 title: "Add missing footnote".into(),
275 kind: CodeActionKind::Quickfix,
276 edits: vec![TextEdit {
277 range: r(10, 0, 10, 0),
278 new_text: "[^1]: ...\n".into(),
279 uri: None,
280 }],
281 };
282 let s = serde_json::to_string(&a).unwrap();
283 let back: CodeAction = serde_json::from_str(&s).unwrap();
284 assert_eq!(back, a);
285 }
286
287 #[test]
288 fn unknown_severity_falls_back_to_info() {
289 let s: DiagnosticSeverity = serde_json::from_str(r#""trace""#).unwrap();
290 assert_eq!(s, DiagnosticSeverity::Info);
291 }
292
293 #[test]
294 fn unknown_completion_kind_falls_back_to_value() {
295 let k: CompletionKind = serde_json::from_str(r#""template""#).unwrap();
296 assert_eq!(k, CompletionKind::Value);
297 }
298
299 #[test]
300 fn unknown_code_action_kind_falls_back_to_refactor() {
301 let k: CodeActionKind = serde_json::from_str(r#""rename""#).unwrap();
302 assert_eq!(k, CodeActionKind::Refactor);
303 }
304
305 #[test]
306 fn unknown_hover_format_falls_back_to_plaintext() {
307 let f: HoverFormat = serde_json::from_str(r#""asciidoc""#).unwrap();
308 assert_eq!(f, HoverFormat::Plaintext);
309 }
310}