1use alloc::{
7 collections::BTreeMap,
8 string::{String, ToString},
9 vec::Vec,
10};
11
12type MessageId = u64;
14
15#[derive(Debug, Clone, PartialEq)]
17pub(crate) enum JsonValue {
18 Null,
19 Bool(bool),
20 Number(i64),
21 Str(String),
22 Array(Vec<JsonValue>),
23 Object(BTreeMap<String, JsonValue>),
24}
25
26impl JsonValue {
27 pub(crate) fn as_str(&self) -> Option<&str> {
28 match self {
29 Self::Str(s) => Some(s),
30 _ => None,
31 }
32 }
33
34 pub(crate) fn as_i64(&self) -> Option<i64> {
35 match self {
36 Self::Number(n) => Some(*n),
37 _ => None,
38 }
39 }
40
41 pub(crate) fn as_object(&self) -> Option<&BTreeMap<String, JsonValue>> {
42 match self {
43 Self::Object(m) => Some(m),
44 _ => None,
45 }
46 }
47
48 pub(crate) fn get(&self, key: &str) -> Option<&JsonValue> {
49 self.as_object()?.get(key)
50 }
51
52 pub(crate) fn to_json(&self) -> String {
54 match self {
55 Self::Null => "null".to_string(),
56 Self::Bool(b) => if *b { "true" } else { "false" }.to_string(),
57 Self::Number(n) => alloc::format!("{}", n),
58 Self::Str(s) => {
59 let escaped = s
60 .replace('\\', "\\\\")
61 .replace('"', "\\\"")
62 .replace('\n', "\\n")
63 .replace('\r', "\\r")
64 .replace('\t', "\\t");
65 alloc::format!("\"{}\"", escaped)
66 }
67 Self::Array(arr) => {
68 let items: Vec<String> = arr.iter().map(|v| v.to_json()).collect();
69 alloc::format!("[{}]", items.join(","))
70 }
71 Self::Object(map) => {
72 let items: Vec<String> = map
73 .iter()
74 .map(|(k, v)| alloc::format!("\"{}\":{}", k, v.to_json()))
75 .collect();
76 alloc::format!("{{{}}}", items.join(","))
77 }
78 }
79 }
80}
81
82pub(crate) fn parse_json(input: &str) -> Option<JsonValue> {
84 let trimmed = input.trim();
85 let (val, _) = parse_value(trimmed.as_bytes(), 0)?;
86 Some(val)
87}
88
89fn parse_value(data: &[u8], mut pos: usize) -> Option<(JsonValue, usize)> {
90 pos = skip_whitespace(data, pos);
91 if pos >= data.len() {
92 return None;
93 }
94
95 match data[pos] {
96 b'"' => parse_string(data, pos),
97 b'{' => parse_object(data, pos),
98 b'[' => parse_array(data, pos),
99 b't' | b'f' => parse_bool(data, pos),
100 b'n' => parse_null(data, pos),
101 b'-' | b'0'..=b'9' => parse_number(data, pos),
102 _ => None,
103 }
104}
105
106fn skip_whitespace(data: &[u8], mut pos: usize) -> usize {
107 while pos < data.len() && matches!(data[pos], b' ' | b'\t' | b'\n' | b'\r') {
108 pos += 1;
109 }
110 pos
111}
112
113fn parse_string(data: &[u8], mut pos: usize) -> Option<(JsonValue, usize)> {
114 if data[pos] != b'"' {
115 return None;
116 }
117 pos += 1;
118 let mut s = String::new();
119 while pos < data.len() && data[pos] != b'"' {
120 if data[pos] == b'\\' && pos + 1 < data.len() {
121 pos += 1;
122 match data[pos] {
123 b'"' => s.push('"'),
124 b'\\' => s.push('\\'),
125 b'n' => s.push('\n'),
126 b'r' => s.push('\r'),
127 b't' => s.push('\t'),
128 _ => {
129 s.push('\\');
130 s.push(data[pos] as char);
131 }
132 }
133 } else {
134 s.push(data[pos] as char);
135 }
136 pos += 1;
137 }
138 if pos >= data.len() {
139 return None;
140 }
141 pos += 1; Some((JsonValue::Str(s), pos))
143}
144
145fn parse_object(data: &[u8], mut pos: usize) -> Option<(JsonValue, usize)> {
146 pos += 1; let mut map = BTreeMap::new();
148 pos = skip_whitespace(data, pos);
149
150 if pos < data.len() && data[pos] == b'}' {
151 return Some((JsonValue::Object(map), pos + 1));
152 }
153
154 loop {
155 pos = skip_whitespace(data, pos);
156 let (key_val, new_pos) = parse_string(data, pos)?;
157 pos = new_pos;
158 let key = match key_val {
159 JsonValue::Str(s) => s,
160 _ => return None,
161 };
162
163 pos = skip_whitespace(data, pos);
164 if pos >= data.len() || data[pos] != b':' {
165 return None;
166 }
167 pos += 1;
168
169 let (val, new_pos) = parse_value(data, pos)?;
170 pos = new_pos;
171 map.insert(key, val);
172
173 pos = skip_whitespace(data, pos);
174 if pos >= data.len() {
175 return None;
176 }
177 if data[pos] == b'}' {
178 return Some((JsonValue::Object(map), pos + 1));
179 }
180 if data[pos] == b',' {
181 pos += 1;
182 }
183 }
184}
185
186fn parse_array(data: &[u8], mut pos: usize) -> Option<(JsonValue, usize)> {
187 pos += 1; let mut arr = Vec::new();
189 pos = skip_whitespace(data, pos);
190
191 if pos < data.len() && data[pos] == b']' {
192 return Some((JsonValue::Array(arr), pos + 1));
193 }
194
195 loop {
196 let (val, new_pos) = parse_value(data, pos)?;
197 pos = new_pos;
198 arr.push(val);
199
200 pos = skip_whitespace(data, pos);
201 if pos >= data.len() {
202 return None;
203 }
204 if data[pos] == b']' {
205 return Some((JsonValue::Array(arr), pos + 1));
206 }
207 if data[pos] == b',' {
208 pos += 1;
209 }
210 }
211}
212
213fn parse_bool(data: &[u8], pos: usize) -> Option<(JsonValue, usize)> {
214 if data[pos..].starts_with(b"true") {
215 Some((JsonValue::Bool(true), pos + 4))
216 } else if data[pos..].starts_with(b"false") {
217 Some((JsonValue::Bool(false), pos + 5))
218 } else {
219 None
220 }
221}
222
223fn parse_null(data: &[u8], pos: usize) -> Option<(JsonValue, usize)> {
224 if data[pos..].starts_with(b"null") {
225 Some((JsonValue::Null, pos + 4))
226 } else {
227 None
228 }
229}
230
231fn parse_number(data: &[u8], mut pos: usize) -> Option<(JsonValue, usize)> {
232 let start = pos;
233 if pos < data.len() && data[pos] == b'-' {
234 pos += 1;
235 }
236 while pos < data.len() && data[pos].is_ascii_digit() {
237 pos += 1;
238 }
239 let num_str = core::str::from_utf8(&data[start..pos]).ok()?;
240 let num: i64 = num_str.parse().ok()?;
241 Some((JsonValue::Number(num), pos))
242}
243
244#[derive(Debug, Clone)]
250pub(crate) struct CompletionItem {
251 pub(crate) label: String,
252 pub(crate) kind: CompletionKind,
253 pub(crate) detail: Option<String>,
254 pub(crate) insert_text: Option<String>,
255}
256
257#[derive(Debug, Clone, Copy, PartialEq, Eq)]
258pub(crate) enum CompletionKind {
259 Text = 1,
260 Method = 2,
261 Function = 3,
262 Constructor = 4,
263 Field = 5,
264 Variable = 6,
265 Class = 7,
266 Interface = 8,
267 Module = 9,
268 Property = 10,
269 Keyword = 14,
270 Snippet = 15,
271}
272
273#[derive(Debug, Clone)]
275pub(crate) struct Diagnostic {
276 pub(crate) range_start_line: u32,
277 pub(crate) range_start_col: u32,
278 pub(crate) range_end_line: u32,
279 pub(crate) range_end_col: u32,
280 pub(crate) severity: DiagnosticSeverity,
281 pub(crate) message: String,
282 pub(crate) source: Option<String>,
283}
284
285#[derive(Debug, Clone, Copy, PartialEq, Eq)]
286pub(crate) enum DiagnosticSeverity {
287 Error = 1,
288 Warning = 2,
289 Information = 3,
290 Hint = 4,
291}
292
293#[derive(Debug, Clone)]
295pub(crate) struct Location {
296 pub(crate) uri: String,
297 pub(crate) line: u32,
298 pub(crate) col: u32,
299}
300
301#[derive(Debug, Clone)]
303pub(crate) struct HoverResult {
304 pub(crate) contents: String,
305 pub(crate) range: Option<(u32, u32, u32, u32)>,
306}
307
308pub(crate) struct LspClient {
310 next_id: MessageId,
311 pub(crate) server_capabilities: Option<JsonValue>,
312 pub(crate) diagnostics: Vec<Diagnostic>,
313 initialized: bool,
314}
315
316impl Default for LspClient {
317 fn default() -> Self {
318 Self::new()
319 }
320}
321
322impl LspClient {
323 pub(crate) fn new() -> Self {
324 Self {
325 next_id: 1,
326 server_capabilities: None,
327 diagnostics: Vec::new(),
328 initialized: false,
329 }
330 }
331
332 fn next_id(&mut self) -> MessageId {
333 let id = self.next_id;
334 self.next_id += 1;
335 id
336 }
337
338 pub(crate) fn build_request(&mut self, method: &str, params: JsonValue) -> String {
340 let id = self.next_id();
341 let mut obj = BTreeMap::new();
342 obj.insert("jsonrpc".to_string(), JsonValue::Str("2.0".to_string()));
343 obj.insert("id".to_string(), JsonValue::Number(id as i64));
344 obj.insert("method".to_string(), JsonValue::Str(method.to_string()));
345 obj.insert("params".to_string(), params);
346
347 let json = JsonValue::Object(obj).to_json();
348 alloc::format!("Content-Length: {}\r\n\r\n{}", json.len(), json)
349 }
350
351 pub(crate) fn build_notification(&self, method: &str, params: JsonValue) -> String {
353 let mut obj = BTreeMap::new();
354 obj.insert("jsonrpc".to_string(), JsonValue::Str("2.0".to_string()));
355 obj.insert("method".to_string(), JsonValue::Str(method.to_string()));
356 obj.insert("params".to_string(), params);
357
358 let json = JsonValue::Object(obj).to_json();
359 alloc::format!("Content-Length: {}\r\n\r\n{}", json.len(), json)
360 }
361
362 pub(crate) fn build_initialize(&mut self, root_uri: &str) -> String {
364 let mut capabilities = BTreeMap::new();
365 let mut text_doc = BTreeMap::new();
366 let mut completion = BTreeMap::new();
367 completion.insert("dynamicRegistration".to_string(), JsonValue::Bool(false));
368 text_doc.insert("completion".to_string(), JsonValue::Object(completion));
369 capabilities.insert("textDocument".to_string(), JsonValue::Object(text_doc));
370
371 let mut params = BTreeMap::new();
372 params.insert("processId".to_string(), JsonValue::Number(1));
373 params.insert("rootUri".to_string(), JsonValue::Str(root_uri.to_string()));
374 params.insert("capabilities".to_string(), JsonValue::Object(capabilities));
375
376 self.build_request("initialize", JsonValue::Object(params))
377 }
378
379 pub(crate) fn parse_response(&mut self, data: &str) -> Option<JsonValue> {
381 let body_start = data.find("\r\n\r\n")?;
383 let body = &data[body_start + 4..];
384 let val = parse_json(body)?;
385
386 if let Some(result) = val.get("result") {
388 if let Some(caps) = result.get("capabilities") {
389 self.server_capabilities = Some(caps.clone());
390 self.initialized = true;
391 }
392 }
393
394 Some(val)
395 }
396
397 pub(crate) fn parse_completions(&self, result: &JsonValue) -> Vec<CompletionItem> {
399 let mut items = Vec::new();
400
401 let arr = match result {
402 JsonValue::Array(a) => a,
403 JsonValue::Object(obj) => match obj.get("items") {
404 Some(JsonValue::Array(a)) => a,
405 _ => return items,
406 },
407 _ => return items,
408 };
409
410 for item in arr {
411 if let Some(obj) = item.as_object() {
412 let label = obj
413 .get("label")
414 .and_then(|v| v.as_str())
415 .unwrap_or("")
416 .to_string();
417
418 let kind_num = obj.get("kind").and_then(|v| v.as_i64()).unwrap_or(1);
419
420 let kind = match kind_num {
421 2 => CompletionKind::Method,
422 3 => CompletionKind::Function,
423 4 => CompletionKind::Constructor,
424 5 => CompletionKind::Field,
425 6 => CompletionKind::Variable,
426 7 => CompletionKind::Class,
427 8 => CompletionKind::Interface,
428 9 => CompletionKind::Module,
429 10 => CompletionKind::Property,
430 14 => CompletionKind::Keyword,
431 15 => CompletionKind::Snippet,
432 _ => CompletionKind::Text,
433 };
434
435 let detail = obj
436 .get("detail")
437 .and_then(|v| v.as_str())
438 .map(|s| s.to_string());
439
440 let insert_text = obj
441 .get("insertText")
442 .and_then(|v| v.as_str())
443 .map(|s| s.to_string());
444
445 items.push(CompletionItem {
446 label,
447 kind,
448 detail,
449 insert_text,
450 });
451 }
452 }
453
454 items
455 }
456
457 pub(crate) fn is_initialized(&self) -> bool {
458 self.initialized
459 }
460}
461
462#[cfg(test)]
467mod tests {
468 #[allow(unused_imports)]
469 use alloc::vec;
470
471 use super::*;
472
473 #[test]
474 fn test_parse_json_null() {
475 assert_eq!(parse_json("null"), Some(JsonValue::Null));
476 }
477
478 #[test]
479 fn test_parse_json_bool() {
480 assert_eq!(parse_json("true"), Some(JsonValue::Bool(true)));
481 assert_eq!(parse_json("false"), Some(JsonValue::Bool(false)));
482 }
483
484 #[test]
485 fn test_parse_json_number() {
486 assert_eq!(parse_json("42"), Some(JsonValue::Number(42)));
487 assert_eq!(parse_json("-1"), Some(JsonValue::Number(-1)));
488 }
489
490 #[test]
491 fn test_parse_json_string() {
492 assert_eq!(
493 parse_json("\"hello\""),
494 Some(JsonValue::Str("hello".to_string()))
495 );
496 }
497
498 #[test]
499 fn test_parse_json_array() {
500 let val = parse_json("[1, 2, 3]").unwrap();
501 match val {
502 JsonValue::Array(arr) => assert_eq!(arr.len(), 3),
503 _ => panic!("Expected array"),
504 }
505 }
506
507 #[test]
508 fn test_parse_json_object() {
509 let val = parse_json("{\"key\": \"value\"}").unwrap();
510 assert_eq!(val.get("key"), Some(&JsonValue::Str("value".to_string())));
511 }
512
513 #[test]
514 fn test_json_serialize() {
515 let val = JsonValue::Str("hello".to_string());
516 assert_eq!(val.to_json(), "\"hello\"");
517
518 let val = JsonValue::Number(42);
519 assert_eq!(val.to_json(), "42");
520 }
521
522 #[test]
523 fn test_lsp_client_build_request() {
524 let mut client = LspClient::new();
525 let req = client.build_request("test/method", JsonValue::Null);
526 assert!(req.contains("Content-Length:"));
527 assert!(req.contains("test/method"));
528 assert!(req.contains("\"id\":1"));
529 }
530
531 #[test]
532 fn test_lsp_client_initialize() {
533 let mut client = LspClient::new();
534 let req = client.build_initialize("file:///workspace");
535 assert!(req.contains("initialize"));
536 assert!(req.contains("file:///workspace"));
537 }
538
539 #[test]
540 fn test_lsp_client_not_initialized() {
541 let client = LspClient::new();
542 assert!(!client.is_initialized());
543 }
544
545 #[test]
546 fn test_parse_completions_empty() {
547 let client = LspClient::new();
548 let result = JsonValue::Array(Vec::new());
549 let items = client.parse_completions(&result);
550 assert!(items.is_empty());
551 }
552
553 #[test]
554 fn test_parse_completions_items() {
555 let client = LspClient::new();
556 let mut item = BTreeMap::new();
557 item.insert("label".to_string(), JsonValue::Str("println!".to_string()));
558 item.insert("kind".to_string(), JsonValue::Number(3)); let result = JsonValue::Array(vec![JsonValue::Object(item)]);
561 let items = client.parse_completions(&result);
562 assert_eq!(items.len(), 1);
563 assert_eq!(items[0].label, "println!");
564 assert_eq!(items[0].kind, CompletionKind::Function);
565 }
566
567 #[test]
568 fn test_diagnostic_severity() {
569 assert_eq!(DiagnosticSeverity::Error as u8, 1);
570 assert_eq!(DiagnosticSeverity::Warning as u8, 2);
571 }
572
573 #[test]
574 fn test_json_escape() {
575 let val = JsonValue::Str("line1\nline2".to_string());
576 let json = val.to_json();
577 assert!(json.contains("\\n"));
578 }
579}