1use crate::types::*;
8use serde_json::{json, Value};
9use tracing::debug;
10
11pub struct ResponseFormatter;
13
14impl ResponseFormatter {
15 pub fn format_completions(items: &[CompletionItem]) -> Value {
17 debug!("Formatting {} completion items for IDE", items.len());
18
19 let formatted_items: Vec<Value> = items
20 .iter()
21 .map(|item| {
22 json!({
23 "label": item.label,
24 "kind": Self::format_completion_kind(item.kind),
25 "detail": item.detail,
26 "documentation": item.documentation,
27 "insertText": item.insert_text,
28 })
29 })
30 .collect();
31
32 json!({
33 "items": formatted_items,
34 "isIncomplete": false,
35 })
36 }
37
38 pub fn format_diagnostics(diagnostics: &[Diagnostic]) -> Value {
40 debug!("Formatting {} diagnostics for IDE", diagnostics.len());
41
42 let formatted_diagnostics: Vec<Value> = diagnostics
43 .iter()
44 .map(|diag| {
45 json!({
46 "range": {
47 "start": {
48 "line": diag.range.start.line,
49 "character": diag.range.start.character,
50 },
51 "end": {
52 "line": diag.range.end.line,
53 "character": diag.range.end.character,
54 },
55 },
56 "severity": Self::format_diagnostic_severity(diag.severity),
57 "message": diag.message,
58 "source": diag.source,
59 })
60 })
61 .collect();
62
63 json!({
64 "diagnostics": formatted_diagnostics,
65 })
66 }
67
68 pub fn format_hover(hover: &Option<Hover>) -> Value {
70 debug!("Formatting hover information for IDE");
71
72 match hover {
73 Some(h) => {
74 let range = h.range.map(|r| {
75 json!({
76 "start": {
77 "line": r.start.line,
78 "character": r.start.character,
79 },
80 "end": {
81 "line": r.end.line,
82 "character": r.end.character,
83 },
84 })
85 });
86
87 json!({
88 "contents": h.contents,
89 "range": range,
90 })
91 }
92 None => json!(null),
93 }
94 }
95
96 pub fn format_definition(location: &Option<Location>) -> Value {
98 debug!("Formatting definition location for IDE");
99
100 match location {
101 Some(loc) => {
102 json!({
103 "uri": loc.file_path,
104 "range": {
105 "start": {
106 "line": loc.range.start.line,
107 "character": loc.range.start.character,
108 },
109 "end": {
110 "line": loc.range.end.line,
111 "character": loc.range.end.character,
112 },
113 },
114 })
115 }
116 None => json!(null),
117 }
118 }
119
120 fn format_completion_kind(kind: CompletionItemKind) -> u32 {
122 match kind {
123 CompletionItemKind::Text => 1,
124 CompletionItemKind::Method => 2,
125 CompletionItemKind::Function => 3,
126 CompletionItemKind::Constructor => 4,
127 CompletionItemKind::Field => 5,
128 CompletionItemKind::Variable => 6,
129 CompletionItemKind::Class => 7,
130 CompletionItemKind::Interface => 8,
131 CompletionItemKind::Module => 9,
132 CompletionItemKind::Property => 10,
133 CompletionItemKind::Unit => 11,
134 CompletionItemKind::Value => 12,
135 CompletionItemKind::Enum => 13,
136 CompletionItemKind::Keyword => 14,
137 CompletionItemKind::Snippet => 15,
138 CompletionItemKind::Color => 16,
139 CompletionItemKind::File => 17,
140 CompletionItemKind::Reference => 18,
141 CompletionItemKind::Folder => 19,
142 CompletionItemKind::EnumMember => 20,
143 CompletionItemKind::Constant => 21,
144 CompletionItemKind::Struct => 22,
145 CompletionItemKind::Event => 23,
146 CompletionItemKind::Operator => 24,
147 CompletionItemKind::TypeParameter => 25,
148 }
149 }
150
151 fn format_diagnostic_severity(severity: DiagnosticSeverity) -> u32 {
153 match severity {
154 DiagnosticSeverity::Error => 1,
155 DiagnosticSeverity::Warning => 2,
156 DiagnosticSeverity::Information => 3,
157 DiagnosticSeverity::Hint => 4,
158 }
159 }
160
161 pub fn format_for_vscode(response_type: &str, data: Value) -> Value {
163 debug!("Formatting response for VS Code: {}", response_type);
164
165 json!({
166 "jsonrpc": "2.0",
167 "result": data,
168 })
169 }
170
171 pub fn format_for_terminal(response_type: &str, data: Value) -> Value {
173 debug!("Formatting response for terminal editor: {}", response_type);
174
175 json!({
176 "type": response_type,
177 "data": data,
178 })
179 }
180
181 pub fn format_error(code: i32, message: &str) -> Value {
183 debug!("Formatting error response: code={}, message={}", code, message);
184
185 json!({
186 "jsonrpc": "2.0",
187 "error": {
188 "code": code,
189 "message": message,
190 },
191 })
192 }
193}
194
195#[cfg(test)]
196mod tests {
197 use super::*;
198
199 #[test]
200 fn test_format_completions() {
201 let items = vec![
202 CompletionItem {
203 label: "test".to_string(),
204 kind: CompletionItemKind::Function,
205 detail: Some("test function".to_string()),
206 documentation: None,
207 insert_text: "test()".to_string(),
208 },
209 CompletionItem {
210 label: "hello".to_string(),
211 kind: CompletionItemKind::Variable,
212 detail: None,
213 documentation: Some("hello variable".to_string()),
214 insert_text: "hello".to_string(),
215 },
216 ];
217
218 let result = ResponseFormatter::format_completions(&items);
219 assert!(result.get("items").is_some());
220 assert_eq!(result["items"].as_array().unwrap().len(), 2);
221 assert_eq!(result["items"][0]["label"], "test");
222 assert_eq!(result["items"][1]["label"], "hello");
223 }
224
225 #[test]
226 fn test_format_diagnostics() {
227 let diagnostics = vec![
228 Diagnostic {
229 range: Range {
230 start: Position {
231 line: 1,
232 character: 0,
233 },
234 end: Position {
235 line: 1,
236 character: 10,
237 },
238 },
239 severity: DiagnosticSeverity::Error,
240 message: "Test error".to_string(),
241 source: "test".to_string(),
242 },
243 ];
244
245 let result = ResponseFormatter::format_diagnostics(&diagnostics);
246 assert!(result.get("diagnostics").is_some());
247 assert_eq!(result["diagnostics"].as_array().unwrap().len(), 1);
248 assert_eq!(result["diagnostics"][0]["message"], "Test error");
249 assert_eq!(result["diagnostics"][0]["severity"], 1); }
251
252 #[test]
253 fn test_format_hover_with_content() {
254 let hover = Some(Hover {
255 contents: "test hover".to_string(),
256 range: Some(Range {
257 start: Position {
258 line: 1,
259 character: 0,
260 },
261 end: Position {
262 line: 1,
263 character: 4,
264 },
265 }),
266 });
267
268 let result = ResponseFormatter::format_hover(&hover);
269 assert_eq!(result["contents"], "test hover");
270 assert!(result.get("range").is_some());
271 }
272
273 #[test]
274 fn test_format_hover_empty() {
275 let hover: Option<Hover> = None;
276
277 let result = ResponseFormatter::format_hover(&hover);
278 assert!(result.is_null());
279 }
280
281 #[test]
282 fn test_format_definition_with_location() {
283 let location = Some(Location {
284 file_path: "src/main.rs".to_string(),
285 range: Range {
286 start: Position {
287 line: 10,
288 character: 0,
289 },
290 end: Position {
291 line: 10,
292 character: 5,
293 },
294 },
295 });
296
297 let result = ResponseFormatter::format_definition(&location);
298 assert_eq!(result["uri"], "src/main.rs");
299 assert_eq!(result["range"]["start"]["line"], 10);
300 }
301
302 #[test]
303 fn test_format_definition_empty() {
304 let location: Option<Location> = None;
305
306 let result = ResponseFormatter::format_definition(&location);
307 assert!(result.is_null());
308 }
309
310 #[test]
311 fn test_format_completion_kind() {
312 assert_eq!(ResponseFormatter::format_completion_kind(CompletionItemKind::Text), 1);
313 assert_eq!(ResponseFormatter::format_completion_kind(CompletionItemKind::Function), 3);
314 assert_eq!(ResponseFormatter::format_completion_kind(CompletionItemKind::Variable), 6);
315 }
316
317 #[test]
318 fn test_format_diagnostic_severity() {
319 assert_eq!(ResponseFormatter::format_diagnostic_severity(DiagnosticSeverity::Error), 1);
320 assert_eq!(ResponseFormatter::format_diagnostic_severity(DiagnosticSeverity::Warning), 2);
321 assert_eq!(ResponseFormatter::format_diagnostic_severity(DiagnosticSeverity::Information), 3);
322 assert_eq!(ResponseFormatter::format_diagnostic_severity(DiagnosticSeverity::Hint), 4);
323 }
324
325 #[test]
326 fn test_format_for_vscode() {
327 let data = json!({"test": "data"});
328 let result = ResponseFormatter::format_for_vscode("completion", data);
329 assert_eq!(result["jsonrpc"], "2.0");
330 assert!(result.get("result").is_some());
331 }
332
333 #[test]
334 fn test_format_for_terminal() {
335 let data = json!({"test": "data"});
336 let result = ResponseFormatter::format_for_terminal("completion", data);
337 assert_eq!(result["type"], "completion");
338 assert!(result.get("data").is_some());
339 }
340
341 #[test]
342 fn test_format_error() {
343 let result = ResponseFormatter::format_error(-32600, "Invalid Request");
344 assert_eq!(result["jsonrpc"], "2.0");
345 assert_eq!(result["error"]["code"], -32600);
346 assert_eq!(result["error"]["message"], "Invalid Request");
347 }
348}