1use serde::{Deserialize, Serialize};
11use serde_json::Value;
12use std::collections::HashMap;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
20pub struct Position {
21 pub line: u32,
23 pub character: u32,
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
29pub struct Range {
30 pub start: Position,
31 pub end: Position,
32}
33
34#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
36pub struct Location {
37 pub uri: String,
38 pub range: Range,
39}
40
41#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
47pub struct TextDocumentIdentifier {
48 pub uri: String,
49}
50
51#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
53#[serde(rename_all = "camelCase")]
54pub struct TextDocumentItem {
55 pub uri: String,
56 pub language_id: String,
57 pub version: i32,
58 pub text: String,
59}
60
61#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
63#[serde(rename_all = "camelCase")]
64pub struct TextDocumentPositionParams {
65 pub text_document: TextDocumentIdentifier,
66 pub position: Position,
67}
68
69#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
75pub struct HoverResult {
76 pub contents: HoverContents,
77 #[serde(skip_serializing_if = "Option::is_none")]
78 pub range: Option<Range>,
79}
80
81#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
83#[serde(untagged)]
84pub enum HoverContents {
85 Scalar(MarkedString),
87 Markup(MarkupContent),
89 Array(Vec<MarkedString>),
91}
92
93#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
95#[serde(untagged)]
96pub enum MarkedString {
97 String(String),
99 LanguageString { language: String, value: String },
101}
102
103#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
105pub struct MarkupContent {
106 pub kind: String,
108 pub value: String,
109}
110
111#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
118#[serde(rename_all = "camelCase")]
119pub struct CompletionItem {
120 pub label: String,
122 #[serde(skip_serializing_if = "Option::is_none")]
124 pub kind: Option<u32>,
125 #[serde(skip_serializing_if = "Option::is_none")]
127 pub detail: Option<String>,
128 #[serde(skip_serializing_if = "Option::is_none")]
130 pub documentation: Option<Value>,
131 #[serde(skip_serializing_if = "Option::is_none")]
133 pub insert_text: Option<String>,
134}
135
136#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
138#[serde(untagged)]
139pub enum CompletionResponse {
140 Array(Vec<CompletionItem>),
142 List(CompletionList),
144}
145
146#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
148#[serde(rename_all = "camelCase")]
149pub struct CompletionList {
150 pub is_incomplete: bool,
152 pub items: Vec<CompletionItem>,
153}
154
155#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
161#[repr(u8)]
162pub enum DiagnosticSeverity {
163 Error = 1,
164 Warning = 2,
165 Information = 3,
166 Hint = 4,
167}
168
169impl Serialize for DiagnosticSeverity {
170 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
171 where
172 S: serde::Serializer,
173 {
174 serializer.serialize_u8(*self as u8)
175 }
176}
177
178impl<'de> Deserialize<'de> for DiagnosticSeverity {
179 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
180 where
181 D: serde::Deserializer<'de>,
182 {
183 let value = u8::deserialize(deserializer)?;
184 match value {
185 1 => Ok(DiagnosticSeverity::Error),
186 2 => Ok(DiagnosticSeverity::Warning),
187 3 => Ok(DiagnosticSeverity::Information),
188 4 => Ok(DiagnosticSeverity::Hint),
189 other => Err(serde::de::Error::custom(format!(
190 "invalid DiagnosticSeverity value: {other}"
191 ))),
192 }
193 }
194}
195
196#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
198pub struct Diagnostic {
199 pub range: Range,
200 #[serde(skip_serializing_if = "Option::is_none")]
201 pub severity: Option<DiagnosticSeverity>,
202 pub message: String,
203 #[serde(skip_serializing_if = "Option::is_none")]
204 pub source: Option<String>,
205 #[serde(skip_serializing_if = "Option::is_none")]
206 pub code: Option<Value>,
207}
208
209#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
211pub struct PublishDiagnosticsParams {
212 pub uri: String,
213 pub diagnostics: Vec<Diagnostic>,
214}
215
216#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
222#[serde(rename_all = "camelCase")]
223pub struct TextEdit {
224 pub range: Range,
225 pub new_text: String,
226}
227
228#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
230pub struct WorkspaceEdit {
231 #[serde(skip_serializing_if = "Option::is_none")]
232 pub changes: Option<HashMap<String, Vec<TextEdit>>>,
233}
234
235#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
241#[serde(rename_all = "camelCase")]
242pub struct RenameParams {
243 pub text_document: TextDocumentIdentifier,
244 pub position: Position,
245 pub new_name: String,
246}
247
248#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
254#[serde(rename_all = "camelCase")]
255pub struct FormattingOptions {
256 pub tab_size: u32,
257 pub insert_spaces: bool,
258}
259
260#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
262#[serde(rename_all = "camelCase")]
263pub struct DocumentFormattingParams {
264 pub text_document: TextDocumentIdentifier,
265 pub options: FormattingOptions,
266}
267
268#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
274#[serde(rename_all = "camelCase")]
275pub struct ReferenceParams {
276 pub text_document: TextDocumentIdentifier,
277 pub position: Position,
278 pub context: ReferenceContext,
279}
280
281#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
283#[serde(rename_all = "camelCase")]
284pub struct ReferenceContext {
285 pub include_declaration: bool,
286}
287
288#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
294#[serde(rename_all = "camelCase")]
295pub struct InitializeParams {
296 pub process_id: Option<u32>,
299 #[serde(skip_serializing_if = "Option::is_none")]
301 pub root_uri: Option<String>,
302 pub capabilities: ClientCapabilities,
304 #[serde(skip_serializing_if = "Option::is_none")]
306 pub initialization_options: Option<Value>,
307}
308
309#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
313#[serde(rename_all = "camelCase")]
314pub struct ClientCapabilities {
315 #[serde(skip_serializing_if = "Option::is_none")]
317 pub text_document: Option<Value>,
318 #[serde(skip_serializing_if = "Option::is_none")]
320 pub workspace: Option<Value>,
321}
322
323#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
325#[serde(rename_all = "camelCase")]
326pub struct ServerCapabilities {
327 #[serde(skip_serializing_if = "Option::is_none")]
329 pub hover_provider: Option<bool>,
330 #[serde(skip_serializing_if = "Option::is_none")]
332 pub definition_provider: Option<bool>,
333 #[serde(skip_serializing_if = "Option::is_none")]
335 pub references_provider: Option<bool>,
336 #[serde(skip_serializing_if = "Option::is_none")]
338 pub completion_provider: Option<Value>,
339 #[serde(skip_serializing_if = "Option::is_none")]
341 pub rename_provider: Option<Value>,
342 #[serde(skip_serializing_if = "Option::is_none")]
344 pub document_formatting_provider: Option<bool>,
345 #[serde(skip_serializing_if = "Option::is_none")]
347 pub text_document_sync: Option<Value>,
348}
349
350#[cfg(test)]
355mod tests {
356 use super::*;
357 use serde_json::json;
358
359 #[test]
362 fn test_position_round_trip() {
363 let pos = Position {
364 line: 10,
365 character: 5,
366 };
367 let json = serde_json::to_string(&pos).unwrap();
368 let decoded: Position = serde_json::from_str(&json).unwrap();
369 assert_eq!(pos, decoded);
370 }
371
372 #[test]
373 fn test_position_json_shape() {
374 let pos = Position {
375 line: 3,
376 character: 12,
377 };
378 let v: Value = serde_json::to_value(pos).unwrap();
379 assert_eq!(v, json!({"line": 3, "character": 12}));
380 }
381
382 #[test]
383 fn test_range_round_trip() {
384 let range = Range {
385 start: Position {
386 line: 0,
387 character: 0,
388 },
389 end: Position {
390 line: 0,
391 character: 10,
392 },
393 };
394 let json = serde_json::to_string(&range).unwrap();
395 let decoded: Range = serde_json::from_str(&json).unwrap();
396 assert_eq!(range, decoded);
397 }
398
399 #[test]
400 fn test_location_round_trip() {
401 let loc = Location {
402 uri: "file:///src/main.rs".to_string(),
403 range: Range {
404 start: Position {
405 line: 5,
406 character: 0,
407 },
408 end: Position {
409 line: 5,
410 character: 20,
411 },
412 },
413 };
414 let json = serde_json::to_string(&loc).unwrap();
415 let decoded: Location = serde_json::from_str(&json).unwrap();
416 assert_eq!(loc, decoded);
417 }
418
419 #[test]
422 fn test_hover_result_scalar_string() {
423 let hover = HoverResult {
424 contents: HoverContents::Scalar(MarkedString::String("hello world".to_string())),
425 range: None,
426 };
427 let json = serde_json::to_string(&hover).unwrap();
428 let decoded: HoverResult = serde_json::from_str(&json).unwrap();
429 assert_eq!(hover, decoded);
430 }
431
432 #[test]
433 fn test_hover_result_scalar_language_string() {
434 let hover = HoverResult {
435 contents: HoverContents::Scalar(MarkedString::LanguageString {
436 language: "rust".to_string(),
437 value: "fn main() {}".to_string(),
438 }),
439 range: Some(Range {
440 start: Position {
441 line: 1,
442 character: 0,
443 },
444 end: Position {
445 line: 1,
446 character: 12,
447 },
448 }),
449 };
450 let json = serde_json::to_string(&hover).unwrap();
451 let decoded: HoverResult = serde_json::from_str(&json).unwrap();
452 assert_eq!(hover, decoded);
453 }
454
455 #[test]
456 fn test_hover_result_markup_content() {
457 let hover = HoverResult {
458 contents: HoverContents::Markup(MarkupContent {
459 kind: "markdown".to_string(),
460 value: "# Hello\nWorld".to_string(),
461 }),
462 range: None,
463 };
464 let json = serde_json::to_string(&hover).unwrap();
465 let decoded: HoverResult = serde_json::from_str(&json).unwrap();
466 assert_eq!(hover, decoded);
467 }
468
469 #[test]
470 fn test_hover_result_array() {
471 let hover = HoverResult {
472 contents: HoverContents::Array(vec![
473 MarkedString::String("first".to_string()),
474 MarkedString::LanguageString {
475 language: "python".to_string(),
476 value: "print('hi')".to_string(),
477 },
478 ]),
479 range: None,
480 };
481 let json = serde_json::to_string(&hover).unwrap();
482 let decoded: HoverResult = serde_json::from_str(&json).unwrap();
483 assert_eq!(hover, decoded);
484 }
485
486 #[test]
489 fn test_completion_item_full() {
490 let item = CompletionItem {
491 label: "println!".to_string(),
492 kind: Some(3),
493 detail: Some("macro".to_string()),
494 documentation: Some(json!("Prints to stdout.")),
495 insert_text: Some("println!(\"$1\")".to_string()),
496 };
497 let json = serde_json::to_string(&item).unwrap();
498 let decoded: CompletionItem = serde_json::from_str(&json).unwrap();
499 assert_eq!(item, decoded);
500 }
501
502 #[test]
503 fn test_completion_item_minimal() {
504 let item = CompletionItem {
505 label: "my_func".to_string(),
506 kind: None,
507 detail: None,
508 documentation: None,
509 insert_text: None,
510 };
511 let json = serde_json::to_string(&item).unwrap();
512
513 let v: Value = serde_json::from_str(&json).unwrap();
515 assert!(v.get("kind").is_none());
516 assert!(v.get("detail").is_none());
517 assert!(v.get("documentation").is_none());
518 assert!(v.get("insertText").is_none());
519
520 let decoded: CompletionItem = serde_json::from_str(&json).unwrap();
521 assert_eq!(item, decoded);
522 }
523
524 #[test]
525 fn test_completion_item_camel_case() {
526 let item = CompletionItem {
527 label: "foo".to_string(),
528 kind: Some(1),
529 detail: None,
530 documentation: None,
531 insert_text: Some("foo()".to_string()),
532 };
533 let v: Value = serde_json::to_value(&item).unwrap();
534 assert!(v.get("insertText").is_some());
536 assert!(v.get("insert_text").is_none());
537 }
538
539 #[test]
540 fn test_completion_response_array() {
541 let resp = CompletionResponse::Array(vec![CompletionItem {
542 label: "a".to_string(),
543 kind: None,
544 detail: None,
545 documentation: None,
546 insert_text: None,
547 }]);
548 let json = serde_json::to_string(&resp).unwrap();
549 let decoded: CompletionResponse = serde_json::from_str(&json).unwrap();
550 assert_eq!(resp, decoded);
551 }
552
553 #[test]
554 fn test_completion_response_list() {
555 let resp = CompletionResponse::List(CompletionList {
556 is_incomplete: true,
557 items: vec![CompletionItem {
558 label: "b".to_string(),
559 kind: Some(6),
560 detail: None,
561 documentation: None,
562 insert_text: None,
563 }],
564 });
565 let json = serde_json::to_string(&resp).unwrap();
566 let decoded: CompletionResponse = serde_json::from_str(&json).unwrap();
567 assert_eq!(resp, decoded);
568 }
569
570 #[test]
571 fn test_completion_list_camel_case() {
572 let list = CompletionList {
573 is_incomplete: false,
574 items: vec![],
575 };
576 let v: Value = serde_json::to_value(&list).unwrap();
577 assert!(v.get("isIncomplete").is_some());
578 assert!(v.get("is_incomplete").is_none());
579 }
580
581 #[test]
584 fn test_diagnostic_round_trip() {
585 let diag = Diagnostic {
586 range: Range {
587 start: Position {
588 line: 10,
589 character: 4,
590 },
591 end: Position {
592 line: 10,
593 character: 15,
594 },
595 },
596 severity: Some(DiagnosticSeverity::Error),
597 message: "expected `;`".to_string(),
598 source: Some("rustc".to_string()),
599 code: Some(json!("E0308")),
600 };
601 let json = serde_json::to_string(&diag).unwrap();
602 let decoded: Diagnostic = serde_json::from_str(&json).unwrap();
603 assert_eq!(diag, decoded);
604 }
605
606 #[test]
607 fn test_diagnostic_no_optional_fields() {
608 let diag = Diagnostic {
609 range: Range {
610 start: Position {
611 line: 0,
612 character: 0,
613 },
614 end: Position {
615 line: 0,
616 character: 1,
617 },
618 },
619 severity: None,
620 message: "something".to_string(),
621 source: None,
622 code: None,
623 };
624 let json = serde_json::to_string(&diag).unwrap();
625 let v: Value = serde_json::from_str(&json).unwrap();
626 assert!(v.get("severity").is_none());
627 assert!(v.get("source").is_none());
628 assert!(v.get("code").is_none());
629
630 let decoded: Diagnostic = serde_json::from_str(&json).unwrap();
631 assert_eq!(diag, decoded);
632 }
633
634 #[test]
637 fn test_diagnostic_severity_serialize_as_integer() {
638 assert_eq!(
639 serde_json::to_value(DiagnosticSeverity::Error).unwrap(),
640 json!(1)
641 );
642 assert_eq!(
643 serde_json::to_value(DiagnosticSeverity::Warning).unwrap(),
644 json!(2)
645 );
646 assert_eq!(
647 serde_json::to_value(DiagnosticSeverity::Information).unwrap(),
648 json!(3)
649 );
650 assert_eq!(
651 serde_json::to_value(DiagnosticSeverity::Hint).unwrap(),
652 json!(4)
653 );
654 }
655
656 #[test]
657 fn test_diagnostic_severity_deserialize_from_integer() {
658 let err: DiagnosticSeverity = serde_json::from_value(json!(1)).unwrap();
659 assert_eq!(err, DiagnosticSeverity::Error);
660
661 let warn: DiagnosticSeverity = serde_json::from_value(json!(2)).unwrap();
662 assert_eq!(warn, DiagnosticSeverity::Warning);
663
664 let info: DiagnosticSeverity = serde_json::from_value(json!(3)).unwrap();
665 assert_eq!(info, DiagnosticSeverity::Information);
666
667 let hint: DiagnosticSeverity = serde_json::from_value(json!(4)).unwrap();
668 assert_eq!(hint, DiagnosticSeverity::Hint);
669 }
670
671 #[test]
672 fn test_diagnostic_severity_invalid_value() {
673 let result = serde_json::from_value::<DiagnosticSeverity>(json!(0));
674 assert!(result.is_err());
675
676 let result = serde_json::from_value::<DiagnosticSeverity>(json!(5));
677 assert!(result.is_err());
678 }
679
680 #[test]
683 fn test_text_edit_round_trip() {
684 let edit = TextEdit {
685 range: Range {
686 start: Position {
687 line: 2,
688 character: 0,
689 },
690 end: Position {
691 line: 2,
692 character: 5,
693 },
694 },
695 new_text: "hello".to_string(),
696 };
697 let json = serde_json::to_string(&edit).unwrap();
698 let decoded: TextEdit = serde_json::from_str(&json).unwrap();
699 assert_eq!(edit, decoded);
700 }
701
702 #[test]
703 fn test_text_edit_camel_case() {
704 let edit = TextEdit {
705 range: Range {
706 start: Position {
707 line: 0,
708 character: 0,
709 },
710 end: Position {
711 line: 0,
712 character: 0,
713 },
714 },
715 new_text: "inserted".to_string(),
716 };
717 let v: Value = serde_json::to_value(&edit).unwrap();
718 assert!(v.get("newText").is_some());
719 assert!(v.get("new_text").is_none());
720 }
721
722 #[test]
723 fn test_workspace_edit_with_changes() {
724 let mut changes = HashMap::new();
725 changes.insert(
726 "file:///src/lib.rs".to_string(),
727 vec![TextEdit {
728 range: Range {
729 start: Position {
730 line: 0,
731 character: 0,
732 },
733 end: Position {
734 line: 0,
735 character: 3,
736 },
737 },
738 new_text: "pub".to_string(),
739 }],
740 );
741 let ws_edit = WorkspaceEdit {
742 changes: Some(changes),
743 };
744 let json = serde_json::to_string(&ws_edit).unwrap();
745 let decoded: WorkspaceEdit = serde_json::from_str(&json).unwrap();
746 assert_eq!(ws_edit, decoded);
747 }
748
749 #[test]
750 fn test_workspace_edit_empty() {
751 let ws_edit = WorkspaceEdit { changes: None };
752 let json = serde_json::to_string(&ws_edit).unwrap();
753 let v: Value = serde_json::from_str(&json).unwrap();
754 assert!(v.get("changes").is_none());
755
756 let decoded: WorkspaceEdit = serde_json::from_str(&json).unwrap();
757 assert_eq!(ws_edit, decoded);
758 }
759
760 #[test]
763 fn test_text_document_item_camel_case() {
764 let item = TextDocumentItem {
765 uri: "file:///test.rs".to_string(),
766 language_id: "rust".to_string(),
767 version: 1,
768 text: "fn main() {}".to_string(),
769 };
770 let v: Value = serde_json::to_value(&item).unwrap();
771 assert!(v.get("languageId").is_some());
772 assert!(v.get("language_id").is_none());
773 }
774
775 #[test]
776 fn test_text_document_position_params_camel_case() {
777 let params = TextDocumentPositionParams {
778 text_document: TextDocumentIdentifier {
779 uri: "file:///test.rs".to_string(),
780 },
781 position: Position {
782 line: 0,
783 character: 0,
784 },
785 };
786 let v: Value = serde_json::to_value(¶ms).unwrap();
787 assert!(v.get("textDocument").is_some());
788 assert!(v.get("text_document").is_none());
789 }
790
791 #[test]
792 fn test_rename_params_camel_case() {
793 let params = RenameParams {
794 text_document: TextDocumentIdentifier {
795 uri: "file:///test.rs".to_string(),
796 },
797 position: Position {
798 line: 5,
799 character: 10,
800 },
801 new_name: "bar".to_string(),
802 };
803 let v: Value = serde_json::to_value(¶ms).unwrap();
804 assert!(v.get("textDocument").is_some());
805 assert!(v.get("newName").is_some());
806 assert!(v.get("text_document").is_none());
807 assert!(v.get("new_name").is_none());
808 }
809
810 #[test]
811 fn test_formatting_options_camel_case() {
812 let opts = FormattingOptions {
813 tab_size: 4,
814 insert_spaces: true,
815 };
816 let v: Value = serde_json::to_value(&opts).unwrap();
817 assert!(v.get("tabSize").is_some());
818 assert!(v.get("insertSpaces").is_some());
819 assert!(v.get("tab_size").is_none());
820 assert!(v.get("insert_spaces").is_none());
821 }
822
823 #[test]
824 fn test_document_formatting_params_camel_case() {
825 let params = DocumentFormattingParams {
826 text_document: TextDocumentIdentifier {
827 uri: "file:///test.rs".to_string(),
828 },
829 options: FormattingOptions {
830 tab_size: 2,
831 insert_spaces: true,
832 },
833 };
834 let v: Value = serde_json::to_value(¶ms).unwrap();
835 assert!(v.get("textDocument").is_some());
836 }
837
838 #[test]
839 fn test_reference_params_camel_case() {
840 let params = ReferenceParams {
841 text_document: TextDocumentIdentifier {
842 uri: "file:///test.rs".to_string(),
843 },
844 position: Position {
845 line: 3,
846 character: 7,
847 },
848 context: ReferenceContext {
849 include_declaration: true,
850 },
851 };
852 let v: Value = serde_json::to_value(¶ms).unwrap();
853 assert!(v.get("textDocument").is_some());
854 let ctx = v.get("context").unwrap();
855 assert!(ctx.get("includeDeclaration").is_some());
856 assert!(ctx.get("include_declaration").is_none());
857 }
858
859 #[test]
860 fn test_initialize_params_camel_case() {
861 let params = InitializeParams {
862 process_id: Some(1234),
863 root_uri: Some("file:///workspace".to_string()),
864 capabilities: ClientCapabilities::default(),
865 initialization_options: None,
866 };
867 let v: Value = serde_json::to_value(¶ms).unwrap();
868 assert!(v.get("processId").is_some());
869 assert!(v.get("rootUri").is_some());
870 assert!(v.get("process_id").is_none());
871 assert!(v.get("root_uri").is_none());
872 }
873
874 #[test]
875 fn test_server_capabilities_camel_case() {
876 let caps = ServerCapabilities {
877 hover_provider: Some(true),
878 definition_provider: Some(true),
879 references_provider: Some(false),
880 completion_provider: Some(json!({"triggerCharacters": [".", ":"]})),
881 rename_provider: Some(json!(true)),
882 document_formatting_provider: Some(true),
883 text_document_sync: Some(json!(2)),
884 };
885 let v: Value = serde_json::to_value(&caps).unwrap();
886 assert!(v.get("hoverProvider").is_some());
887 assert!(v.get("definitionProvider").is_some());
888 assert!(v.get("referencesProvider").is_some());
889 assert!(v.get("completionProvider").is_some());
890 assert!(v.get("renameProvider").is_some());
891 assert!(v.get("documentFormattingProvider").is_some());
892 assert!(v.get("textDocumentSync").is_some());
893 assert!(v.get("hover_provider").is_none());
895 assert!(v.get("text_document_sync").is_none());
896 }
897
898 #[test]
899 fn test_server_capabilities_omits_none() {
900 let caps = ServerCapabilities::default();
901 let v: Value = serde_json::to_value(&caps).unwrap();
902 assert!(v.get("hoverProvider").is_none());
903 assert!(v.get("definitionProvider").is_none());
904 }
905
906 #[test]
909 fn test_publish_diagnostics_round_trip() {
910 let params = PublishDiagnosticsParams {
911 uri: "file:///src/main.rs".to_string(),
912 diagnostics: vec![Diagnostic {
913 range: Range {
914 start: Position {
915 line: 1,
916 character: 0,
917 },
918 end: Position {
919 line: 1,
920 character: 10,
921 },
922 },
923 severity: Some(DiagnosticSeverity::Warning),
924 message: "unused variable".to_string(),
925 source: Some("rustc".to_string()),
926 code: Some(json!("W0001")),
927 }],
928 };
929 let json = serde_json::to_string(¶ms).unwrap();
930 let decoded: PublishDiagnosticsParams = serde_json::from_str(&json).unwrap();
931 assert_eq!(params, decoded);
932 }
933
934 #[test]
937 fn test_deserialize_hover_from_raw_json() {
938 let raw = r##"{
939 "contents": {"kind": "markdown", "value": "# Docs\nSome info"},
940 "range": {"start": {"line": 0, "character": 0}, "end": {"line": 0, "character": 5}}
941 }"##;
942 let hover: HoverResult = serde_json::from_str(raw).unwrap();
943 assert!(matches!(hover.contents, HoverContents::Markup(_)));
944 assert!(hover.range.is_some());
945 }
946
947 #[test]
948 fn test_deserialize_completion_list_from_raw_json() {
949 let raw = r#"{
950 "isIncomplete": false,
951 "items": [
952 {"label": "println!", "kind": 3, "insertText": "println!(\"$1\")"},
953 {"label": "eprintln!", "kind": 3}
954 ]
955 }"#;
956 let list: CompletionList = serde_json::from_str(raw).unwrap();
957 assert!(!list.is_incomplete);
958 assert_eq!(list.items.len(), 2);
959 assert_eq!(list.items[0].label, "println!");
960 assert!(list.items[0].insert_text.is_some());
961 assert!(list.items[1].insert_text.is_none());
962 }
963
964 #[test]
965 fn test_deserialize_diagnostic_from_raw_json() {
966 let raw = json!({
967 "range": {
968 "start": {"line": 3, "character": 0},
969 "end": {"line": 3, "character": 10}
970 },
971 "severity": 1,
972 "message": "unused variable"
973 });
974 let diag: Diagnostic = serde_json::from_value(raw).unwrap();
975 assert_eq!(diag.severity, Some(DiagnosticSeverity::Error));
976 assert_eq!(diag.message, "unused variable");
977 assert_eq!(diag.range.start.line, 3);
978 }
979
980 #[test]
981 fn test_deserialize_text_edit_from_raw_json() {
982 let raw = json!({
983 "range": {
984 "start": {"line": 0, "character": 0},
985 "end": {"line": 0, "character": 10}
986 },
987 "newText": "fn main() {"
988 });
989 let edit: TextEdit = serde_json::from_value(raw).unwrap();
990 assert_eq!(edit.new_text, "fn main() {");
991 assert_eq!(edit.range.end.character, 10);
992 }
993
994 #[test]
995 fn test_deserialize_workspace_edit_from_raw_json() {
996 let raw = json!({
997 "changes": {
998 "file:///src/main.rs": [
999 {
1000 "range": {
1001 "start": {"line": 10, "character": 4},
1002 "end": {"line": 10, "character": 7}
1003 },
1004 "newText": "new_func"
1005 }
1006 ]
1007 }
1008 });
1009 let edit: WorkspaceEdit = serde_json::from_value(raw).unwrap();
1010 let changes = edit.changes.unwrap();
1011 assert_eq!(changes.len(), 1);
1012 let edits = changes.get("file:///src/main.rs").unwrap();
1013 assert_eq!(edits.len(), 1);
1014 assert_eq!(edits[0].new_text, "new_func");
1015 }
1016
1017 #[test]
1020 fn test_text_document_item_round_trip() {
1021 let item = TextDocumentItem {
1022 uri: "file:///src/main.rs".to_string(),
1023 language_id: "rust".to_string(),
1024 version: 3,
1025 text: "fn main() {}".to_string(),
1026 };
1027 let json = serde_json::to_string(&item).unwrap();
1028 let decoded: TextDocumentItem = serde_json::from_str(&json).unwrap();
1029 assert_eq!(item, decoded);
1030 }
1031
1032 #[test]
1033 fn test_initialize_params_round_trip() {
1034 let params = InitializeParams {
1035 process_id: Some(42),
1036 root_uri: Some("file:///workspace".to_string()),
1037 capabilities: ClientCapabilities {
1038 text_document: Some(json!({"hover": {"contentFormat": ["plaintext"]}})),
1039 workspace: None,
1040 },
1041 initialization_options: Some(json!({"customSetting": true})),
1042 };
1043 let json = serde_json::to_string(¶ms).unwrap();
1044 let decoded: InitializeParams = serde_json::from_str(&json).unwrap();
1045 assert_eq!(params, decoded);
1046 }
1047
1048 #[test]
1049 fn test_initialize_params_null_process_id() {
1050 let params = InitializeParams {
1051 process_id: None,
1052 root_uri: None,
1053 capabilities: ClientCapabilities::default(),
1054 initialization_options: None,
1055 };
1056 let v: Value = serde_json::to_value(¶ms).unwrap();
1057 assert!(v.get("processId").is_some());
1059 assert!(v["processId"].is_null());
1060 assert!(v.get("rootUri").is_none());
1062 assert!(v.get("initializationOptions").is_none());
1063 }
1064
1065 #[test]
1066 fn test_reference_params_round_trip() {
1067 let params = ReferenceParams {
1068 text_document: TextDocumentIdentifier {
1069 uri: "file:///test.rs".to_string(),
1070 },
1071 position: Position {
1072 line: 10,
1073 character: 3,
1074 },
1075 context: ReferenceContext {
1076 include_declaration: false,
1077 },
1078 };
1079 let json = serde_json::to_string(¶ms).unwrap();
1080 let decoded: ReferenceParams = serde_json::from_str(&json).unwrap();
1081 assert_eq!(params, decoded);
1082 }
1083
1084 #[test]
1085 fn test_document_formatting_params_round_trip() {
1086 let params = DocumentFormattingParams {
1087 text_document: TextDocumentIdentifier {
1088 uri: "file:///src/lib.rs".to_string(),
1089 },
1090 options: FormattingOptions {
1091 tab_size: 4,
1092 insert_spaces: true,
1093 },
1094 };
1095 let json = serde_json::to_string(¶ms).unwrap();
1096 let decoded: DocumentFormattingParams = serde_json::from_str(&json).unwrap();
1097 assert_eq!(params, decoded);
1098 }
1099}