Skip to main content

gen_lsp_types/
lib.rs

1mod generated;
2pub mod json_rpc;
3
4pub use generated::*;
5
6/// # Tests for compilation success/failure.
7///
8/// ## Should not leak internal symbols
9///
10/// ```compile_fail
11/// use gen_lsp_types::generated;
12/// ```
13///
14/// ```compile_fail
15/// use gen_lsp_types::deserialize_some;
16/// ```
17///
18/// ```compile_fail
19/// use gen_lsp_types::SemanticToken::deserialize_tokens;
20/// ```
21///
22/// ```
23/// use gen_lsp_types::SemanticToken;
24/// ```
25///
26/// ```compile_fail
27/// use gen_lsp_types::json_rpc::deserialize_some;
28/// ```
29///
30/// ## Should have newtype Lsp(Request|Notification)Method semantics
31///
32/// ```compile_fail
33/// use gen_lsp_types::LspNotificationMethod;
34/// let foo: &str = LspNotificationMethod::CancelRequest;
35/// ```
36mod compiletest {}
37
38/// Tests for default features.
39#[cfg(test)]
40#[cfg(all(not(feature = "url"), not(feature = "fluent-uri")))]
41mod test {
42    #![allow(deprecated)]
43    use std::{
44        borrow::Cow,
45        collections::{HashMap, HashSet},
46    };
47
48    use serde_json::json;
49
50    use crate::*;
51
52    #[test]
53    fn nullable_field() {
54        let tdro = TextDocumentRegistrationOptions {
55            document_selector: None,
56        };
57        let tdro_str = serde_json::to_string(&tdro).unwrap();
58        assert_eq!(tdro_str, r#"{"documentSelector":null}"#);
59
60        let tdro = serde_json::from_str::<TextDocumentRegistrationOptions>(&tdro_str).unwrap();
61        assert_eq!(
62            tdro,
63            TextDocumentRegistrationOptions {
64                document_selector: None
65            }
66        );
67
68        // Missing documentSelector. Be liberal on deserialization, even though this is technically a
69        // noncompliant representation.
70        assert_eq!(tdro, serde_json::from_str("{}").unwrap());
71    }
72
73    #[test]
74    fn nullable_field_default() {
75        let ip = InitializeParams::default();
76
77        let ip_str = serde_json::to_string(&ip).unwrap();
78
79        assert_eq!(
80            ip_str,
81            r#"{"processId":null,"rootUri":null,"capabilities":{}}"#
82        );
83
84        assert_eq!(
85            InitializeParams::default(),
86            serde_json::from_str(&ip_str).unwrap()
87        );
88
89        // Missing processId. Be liberal on deserialization, even though this is technically a
90        // noncompliant representation.
91        let bad_ip_str = r#"{"rootUri":null,"capabilities":{}}"#;
92        assert_eq!(
93            InitializeParams::default(),
94            serde_json::from_str(bad_ip_str).unwrap()
95        );
96    }
97
98    #[test]
99    fn optional_field() {
100        let cp = ColorPresentation {
101            label: "Label".to_string(),
102            text_edit: None,
103            ..Default::default()
104        };
105        let cp_str = serde_json::to_string(&cp).unwrap();
106        assert_eq!(cp_str, r#"{"label":"Label"}"#);
107
108        let cp = serde_json::from_str::<ColorPresentation>(&cp_str).unwrap();
109        assert_eq!(
110            cp,
111            ColorPresentation {
112                label: "Label".to_string(),
113                text_edit: None,
114                ..Default::default()
115            }
116        );
117        // While technically illegal in the spec, this library chooses to be more liberal with
118        // DEserialization, in order to be tolerant of slightly spec-noncompliant communicators.
119        assert_eq!(
120            serde_json::from_str::<ColorPresentation>(r#"{"label":"Label","textEdit":null}"#)
121                .unwrap(),
122            cp
123        );
124    }
125
126    #[test]
127    fn optional_nullable_field() {
128        let wfip = WorkspaceFoldersInitializeParams {
129            workspace_folders: None,
130        };
131
132        let wfip_str = serde_json::to_string(&wfip).unwrap();
133
134        assert_eq!(wfip_str, r"{}");
135
136        assert_eq!(
137            serde_json::from_str::<WorkspaceFoldersInitializeParams>(&wfip_str).unwrap(),
138            wfip
139        );
140
141        let wfip = WorkspaceFoldersInitializeParams {
142            workspace_folders: Some(crate::WorkspaceFolders::Null),
143        };
144        let wfip_str = serde_json::to_string(&wfip).unwrap();
145        assert_eq!(wfip_str, r#"{"workspaceFolders":null}"#);
146        assert_eq!(
147            serde_json::from_str::<WorkspaceFoldersInitializeParams>(&wfip_str).unwrap(),
148            wfip
149        );
150
151        let wfip = WorkspaceFoldersInitializeParams {
152            workspace_folders: Some(crate::WorkspaceFolders::WorkspaceFolderList(Vec::new())),
153        };
154        let wfip_str = serde_json::to_string(&wfip).unwrap();
155        assert_eq!(wfip_str, r#"{"workspaceFolders":[]}"#);
156        assert_eq!(
157            serde_json::from_str::<WorkspaceFoldersInitializeParams>(&wfip_str).unwrap(),
158            wfip
159        );
160    }
161
162    #[test]
163    fn derives() {
164        let pos = Position::default();
165        let table = HashMap::from([(pos, 123)]);
166        assert_eq!(table.get(&pos), Some(&123));
167
168        let range = Range::default();
169        let table = HashSet::from([range]);
170        assert!(table.contains(&range));
171
172        let doc_sym = DocumentSymbol {
173            kind: crate::SymbolKind::Function,
174            name: String::default(),
175            detail: Option::default(),
176            tags: Option::default(),
177            deprecated: Option::default(),
178            range: Range::default(),
179            selection_range: Range::default(),
180            children: Option::default(),
181        };
182        let table = HashSet::from([doc_sym.clone()]);
183        assert!(table.contains(&doc_sym));
184
185        // From
186        let wfip = WorkspaceFoldersInitializeParams {
187            workspace_folders: Some(Vec::new().into()),
188        };
189        let wfip_str = serde_json::to_string(&wfip).unwrap();
190        assert_eq!(wfip_str, r#"{"workspaceFolders":[]}"#);
191        assert_eq!(
192            serde_json::from_str::<WorkspaceFoldersInitializeParams>(&wfip_str).unwrap(),
193            wfip
194        );
195        assert_eq!(WorkspaceFolders::Null, ().into());
196        let wfip = WorkspaceFoldersInitializeParams {
197            workspace_folders: Some(().into()),
198        };
199        let wfip_str = serde_json::to_string(&wfip).unwrap();
200        assert_eq!(wfip_str, r#"{"workspaceFolders":null}"#);
201        assert_eq!(
202            serde_json::from_str::<WorkspaceFoldersInitializeParams>(&wfip_str).unwrap(),
203            wfip
204        );
205        let wfsc = WorkspaceFoldersServerCapabilities {
206            change_notifications: Some("some-noti-id".into()),
207            ..Default::default()
208        };
209        let wfsc_str = serde_json::to_string(&wfsc).unwrap();
210        assert_eq!(wfsc_str, r#"{"changeNotifications":"some-noti-id"}"#);
211        let wfsc = WorkspaceFoldersServerCapabilities {
212            change_notifications: Some(String::from("some-noti-id").into()),
213            ..Default::default()
214        };
215        let wfsc_str = serde_json::to_string(&wfsc).unwrap();
216        assert_eq!(wfsc_str, r#"{"changeNotifications":"some-noti-id"}"#);
217        let wfsc = WorkspaceFoldersServerCapabilities {
218            change_notifications: Some(false.into()),
219            ..Default::default()
220        };
221        let wfsc_str = serde_json::to_string(&wfsc).unwrap();
222        assert_eq!(wfsc_str, r#"{"changeNotifications":false}"#);
223        let wfsc = WorkspaceFoldersServerCapabilities {
224            change_notifications: Some('f'.into()),
225            ..Default::default()
226        };
227        let wfsc_str = serde_json::to_string(&wfsc).unwrap();
228        assert_eq!(wfsc_str, r#"{"changeNotifications":"f"}"#);
229        let boxed_str: Box<str> = Box::from("foo");
230        let wfsc = WorkspaceFoldersServerCapabilities {
231            change_notifications: Some(boxed_str.into()),
232            ..Default::default()
233        };
234        let wfsc_str = serde_json::to_string(&wfsc).unwrap();
235        assert_eq!(wfsc_str, r#"{"changeNotifications":"foo"}"#);
236    }
237
238    #[test]
239    fn special_impls() {
240        let pos = Position {
241            line: 2,
242            character: 0,
243        };
244        let pos2 = Position {
245            line: 1,
246            character: 9,
247        };
248        assert!(pos2 < pos);
249
250        // Copy
251        let pos3 = pos2;
252        assert_eq!(pos3, pos2);
253
254        let range = Range {
255            start: pos2,
256            end: pos,
257        };
258        // Copy
259        let range2 = range;
260        assert_eq!(range2, range);
261
262        let range3 = Range {
263            start: Position::default(),
264            end: Position {
265                line: 999,
266                character: 999,
267            },
268        };
269        assert!(range3 < range2);
270
271        let range4 = Range::default();
272        assert!(range4 < range3);
273
274        let method: String = LspRequestMethod::TextDocumentOnTypeFormatting.into();
275        assert_eq!(method, "textDocument/onTypeFormatting");
276        let method = LspRequestMethod::Shutdown.to_string();
277        assert_eq!(method, "shutdown");
278        let method = LspRequestMethod::Custom("foo").to_string();
279        assert_eq!(method, "foo");
280        let method = LspNotificationMethod::Custom("foo").to_string();
281        assert_eq!(method, "foo");
282        let method = LspNotificationMethod::CancelRequest.to_string();
283        assert_eq!(method, "$/cancelRequest");
284        let method = LspNotificationMethod::WorkspaceDidChangeWatchedFiles.to_string();
285        assert_eq!(method, "workspace/didChangeWatchedFiles");
286
287        let wk = WatchKind::Delete;
288        let wk2 = WatchKind::Create;
289        assert_eq!(wk | wk2, WatchKind::Custom(5));
290        assert_eq!(wk | wk2 | WatchKind::Change, WatchKind::Custom(7));
291        assert_eq!((wk | wk2) & wk, wk);
292        assert_eq!(wk | wk, wk);
293        assert_eq!((wk | wk2 | WatchKind::Change) & (wk | wk2), wk | wk2);
294        assert_eq!(WatchKind::Delete ^ WatchKind::Delete, WatchKind::Custom(0));
295        assert_eq!(WatchKind::Custom(0) ^ WatchKind::Create, WatchKind::Create);
296        let mut wk = WatchKind::Custom(0);
297        wk |= WatchKind::Delete;
298        assert_eq!(wk, WatchKind::Delete);
299        wk &= WatchKind::Create;
300        assert_eq!(wk, WatchKind::Custom(0));
301        wk ^= WatchKind::Delete;
302        assert_eq!(wk, WatchKind::Delete);
303    }
304
305    #[test]
306    fn string_literal_field() {
307        let wdpe = WorkDoneProgressEnd {
308            message: Some("change da world. my final message. goodbye".to_string()),
309        };
310
311        let ser = serde_json::to_string(&wdpe).unwrap();
312        assert_eq!(
313            ser,
314            r#"{"message":"change da world. my final message. goodbye","kind":"end"}"#
315        );
316
317        let deser = serde_json::from_str::<WorkDoneProgressEnd>(&ser).unwrap();
318        assert_eq!(deser, wdpe);
319
320        let fake_ser = r#"{"message":"change da world. my final message. goodbye","kind":"begin"}"#;
321        assert!(serde_json::from_str::<WorkDoneProgressEnd>(fake_ser).is_err());
322
323        let doc_change = CreateFile {
324            uri: "file:///foo.txt".to_string().into(),
325            options: None,
326            annotation_id: None,
327        };
328        let ser = serde_json::to_string(&doc_change).unwrap();
329        assert_eq!(ser, r#"{"uri":"file:///foo.txt","kind":"create"}"#);
330
331        let ser = r#"{"uri":"file:///foo.txt","kind":"create"}"#;
332        let deser = serde_json::from_str::<DocumentChange>(ser).unwrap();
333        assert_eq!(deser, doc_change.into());
334        let ser = r#"{"uri":"file:///foo.txt","kind":"delete","annotationId":"foo"}"#;
335        let deser = serde_json::from_str::<DocumentChange>(ser).unwrap();
336        assert_eq!(
337            deser,
338            DocumentChange::DeleteFile(DeleteFile {
339                uri: crate::Uri("file:///foo.txt".to_string()),
340                options: None,
341                annotation_id: Some(String::from("foo"))
342            })
343        );
344        let bad_ser = r#"{"uri":"file:///foo.txt","kind":"delet"}"#;
345        assert!(serde_json::from_str::<DocumentChange>(bad_ser).is_err());
346    }
347
348    #[test]
349    fn string_enum() {
350        let frk = FoldingRangeKind::Comment;
351        let ser = serde_json::to_string(&frk).unwrap();
352
353        assert_eq!(ser, "\"comment\"");
354        assert_eq!(
355            serde_json::from_str::<FoldingRangeKind>(&ser).unwrap(),
356            FoldingRangeKind::Comment
357        );
358
359        let frk = FoldingRangeKind::Custom(Cow::Borrowed("foo"));
360        let ser = serde_json::to_string(&frk).unwrap();
361
362        assert_eq!(ser, "\"foo\"");
363        assert_eq!(
364            serde_json::from_str::<FoldingRangeKind>(&ser).unwrap(),
365            FoldingRangeKind::Custom(Cow::Borrowed("foo"))
366        );
367        assert_eq!("foo", frk.as_str());
368
369        let mk = MarkupKind::PlainText;
370        assert_eq!("\"plaintext\"", serde_json::to_string(&mk).unwrap());
371        assert!(serde_json::from_str::<MarkupKind>("foo").is_err());
372        assert_eq!("plaintext", mk.as_str());
373    }
374
375    #[test]
376    fn str_enum_into() {
377        const CONSTANT: &str = "my_custom_variant";
378        let _parsed: LspNotificationMethod = CONSTANT.into();
379    }
380
381    #[test]
382    fn int_enum() {
383        let sk = SymbolKind::Namespace;
384        let ser = serde_json::to_string(&sk).unwrap();
385
386        assert_eq!(ser, "3");
387        assert_eq!(
388            serde_json::from_str::<SymbolKind>(&ser).unwrap(),
389            SymbolKind::Namespace
390        );
391        assert!(serde_json::from_str::<SymbolKind>("299").is_ok());
392
393        let wk = WatchKind::Custom(123);
394        let ser = serde_json::to_string(&wk).unwrap();
395
396        assert_eq!(ser, "123");
397        assert_eq!(wk, serde_json::from_str::<WatchKind>(&ser).unwrap());
398        assert_eq!(
399            WatchKind::Change,
400            serde_json::from_str::<WatchKind>("2").unwrap()
401        );
402    }
403
404    #[test]
405    fn request_object_from_request() {
406        let params = TypeDefinitionParams {
407            work_done_progress_params: crate::WorkDoneProgressParams {
408                work_done_token: None,
409            },
410            partial_result_params: crate::PartialResultParams {
411                partial_result_token: None,
412            },
413            text_document_position_params: crate::TextDocumentPositionParams {
414                text_document: crate::TextDocumentIdentifier { uri: "foo".into() },
415                position: Position::default(),
416            },
417        };
418        let req = json_rpc::RequestObject::from_request::<TypeDefinitionRequest>(
419            json_rpc::Id::Number(123),
420            params.clone(),
421        );
422
423        let ser = serde_json::to_string(&req).unwrap();
424
425        assert_eq!(
426            ser,
427            r#"{"jsonrpc":"2.0","id":123,"method":"textDocument/typeDefinition","params":{"position":{"character":0,"line":0},"textDocument":{"uri":"foo"}}}"#
428        );
429        assert_eq!(req.id(), Some(&json_rpc::Id::Number(123)));
430        assert_eq!(req.method(), "textDocument/typeDefinition");
431        assert_eq!(req.params(), Some(&json!(params)));
432    }
433
434    #[test]
435    fn request_object_from_request_no_params() {
436        let req = json_rpc::RequestObject::from_request::<WorkspaceFoldersRequest>(
437            json_rpc::Id::String("foo".into()),
438            (),
439        );
440
441        let ser = serde_json::to_string(&req).unwrap();
442
443        assert_eq!(
444            ser,
445            r#"{"jsonrpc":"2.0","id":"foo","method":"workspace/workspaceFolders"}"#
446        );
447    }
448
449    #[test]
450    fn request_object_from_notification() {
451        let noti = json_rpc::RequestObject::from_notification::<InitializedNotification>(
452            InitializedParams {},
453        );
454
455        let ser = serde_json::to_string(&noti).unwrap();
456
457        assert_eq!(
458            ser,
459            r#"{"jsonrpc":"2.0","method":"initialized","params":{}}"#
460        );
461        assert_eq!(noti.id(), None);
462        assert_eq!(noti.method(), "initialized");
463        assert_eq!(noti.params(), Some(&json!(InitializedParams {})));
464    }
465
466    #[test]
467    fn request_object_from_notification_no_params() {
468        let noti = json_rpc::RequestObject::from_notification::<ExitNotification>(());
469
470        let ser = serde_json::to_string(&noti).unwrap();
471
472        assert_eq!(ser, r#"{"jsonrpc":"2.0","method":"exit"}"#);
473    }
474
475    #[test]
476    fn response_object_from_success() {
477        let id = json_rpc::Id::Number(123);
478
479        let res = json_rpc::ResponseObject::from_success::<ImplementationRequest>(
480            id.clone(),
481            Some(ImplementationResponse::DefinitionLinkList(Vec::new())),
482        );
483
484        let ser = serde_json::to_string(&res).unwrap();
485        assert_eq!(r#"{"jsonrpc":"2.0","result":[],"id":123}"#, &ser);
486        assert_eq!(res, serde_json::from_str(&ser).unwrap());
487
488        let res = json_rpc::ResponseObject::from_success::<ImplementationRequest>(id.clone(), None);
489
490        let ser = serde_json::to_string(&res).unwrap();
491        assert_eq!(r#"{"jsonrpc":"2.0","result":null,"id":123}"#, &ser);
492        assert_eq!(res, serde_json::from_str(&ser).unwrap());
493
494        let res = json_rpc::ResponseObject::from_success::<ImplementationRequest>(id.clone(), None);
495
496        let ser = serde_json::to_string(&res).unwrap();
497        assert_eq!(r#"{"jsonrpc":"2.0","result":null,"id":123}"#, &ser);
498        assert_eq!(res, serde_json::from_str(&ser).unwrap());
499
500        let res = json_rpc::ResponseObject::from_success::<ShowMessageRequest>(id.clone(), None);
501
502        let ser = serde_json::to_string(&res).unwrap();
503        assert_eq!(r#"{"jsonrpc":"2.0","result":null,"id":123}"#, &ser);
504        assert_eq!(res, serde_json::from_str(&ser).unwrap());
505
506        let res = json_rpc::ResponseObject::from_success::<ShowMessageRequest>(
507            id.clone(),
508            Some(MessageActionItem {
509                title: "foo".into(),
510                properties: HashMap::default(),
511            }),
512        );
513
514        let ser = serde_json::to_string(&res).unwrap();
515        assert_eq!(
516            r#"{"jsonrpc":"2.0","result":{"title":"foo"},"id":123}"#,
517            &ser
518        );
519        assert_eq!(res, serde_json::from_str(&ser).unwrap());
520
521        let res = json_rpc::ResponseObject::from_success::<ShowMessageRequest>(
522            id.clone(),
523            Some(MessageActionItem {
524                title: "foo".into(),
525                properties: HashMap::from([(
526                    String::from("bar"),
527                    MessageActionItemProperty::Int(321),
528                )]),
529            }),
530        );
531
532        let ser = serde_json::to_string(&res).unwrap();
533        assert_eq!(
534            r#"{"jsonrpc":"2.0","result":{"bar":321,"title":"foo"},"id":123}"#,
535            &ser
536        );
537        assert_eq!(res, serde_json::from_str(&ser).unwrap());
538
539        let res = json_rpc::ResponseObject::from_success::<CodeLensRefreshRequest>(id, ());
540
541        let ser = serde_json::to_string(&res).unwrap();
542        assert_eq!(r#"{"jsonrpc":"2.0","result":null,"id":123}"#, &ser);
543        assert_eq!(res, serde_json::from_str(&ser).unwrap());
544    }
545
546    #[test]
547    fn response_object_from_error() {
548        let id = json_rpc::Id::Null;
549        let res = json_rpc::ResponseObject::from_error(
550            id,
551            json_rpc::Error {
552                code: crate::ErrorCodes::ParseError,
553                message: "invalid format".into(),
554                data: None,
555            },
556        );
557
558        let ser = serde_json::to_string(&res).unwrap();
559        assert_eq!(
560            r#"{"jsonrpc":"2.0","error":{"code":-32700,"message":"invalid format"},"id":null}"#,
561            &ser
562        );
563
564        let id = json_rpc::Id::String("foo-req".into());
565        let res = json_rpc::ResponseObject::from_error(
566            id,
567            json_rpc::Error {
568                code: crate::ErrorCodes::Custom(-32803),
569                message: "failed to foo the bar".into(),
570                data: Some(json!("hi")),
571            },
572        );
573
574        let ser = serde_json::to_string(&res).unwrap();
575        assert_eq!(
576            r#"{"jsonrpc":"2.0","error":{"code":-32803,"message":"failed to foo the bar","data":"hi"},"id":"foo-req"}"#,
577            &ser
578        );
579
580        let id = json_rpc::Id::String("foo-req".into());
581        let res = json_rpc::ResponseObject::from_error(
582            id,
583            json_rpc::Error {
584                code: crate::ErrorCodes::Custom(crate::LspErrorCodes::ContentModified.into()),
585                message: "failed to foo the bar".into(),
586                data: Some(json!("hi")),
587            },
588        );
589
590        let ser = serde_json::to_string(&res).unwrap();
591        assert_eq!(
592            r#"{"jsonrpc":"2.0","error":{"code":-32801,"message":"failed to foo the bar","data":"hi"},"id":"foo-req"}"#,
593            &ser
594        );
595    }
596
597    #[test]
598    fn structures_new_constructor() {
599        assert_eq!(
600            Position::new(1, 2),
601            Position {
602                line: 1,
603                character: 2
604            }
605        );
606        assert_eq!(
607            Range::new(Position::new(0, 0), Position::new(0, 99)),
608            Range {
609                start: Position::default(),
610                end: Position {
611                    line: 0,
612                    character: 99
613                }
614            }
615        );
616
617        assert_eq!(
618            Location::new(Uri("foo".into()), Range::default()),
619            Location {
620                uri: Uri("foo".into()),
621                range: Range::default()
622            }
623        );
624
625        // For some structures, the new() constructor does not help as much.
626        assert_eq!(
627            Diagnostic::new(
628                Range::default(),
629                Some(DiagnosticSeverity::Warning),
630                None,
631                None,
632                None,
633                "bad".into(),
634                None,
635                None,
636                None
637            ),
638            Diagnostic {
639                range: Range::default(),
640                message: "bad".into(),
641                severity: Some(DiagnosticSeverity::Warning),
642                ..Default::default()
643            }
644        );
645    }
646
647    #[test]
648    fn custom_request_object_methods() {
649        struct ParentModule;
650        impl Request for ParentModule {
651            type Params = ();
652            type Result = Option<DefinitionResponse>;
653            const METHOD: LspRequestMethod<'_> =
654                LspRequestMethod::Custom("experimental/parentModule");
655            const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer;
656        }
657
658        let req =
659            json_rpc::RequestObject::from_request::<ParentModule>(json_rpc::Id::Number(123), ());
660        assert_eq!(
661            r#"{"jsonrpc":"2.0","id":123,"method":"experimental/parentModule"}"#,
662            serde_json::to_string(&req).unwrap()
663        );
664
665        struct ServerStatusNotification;
666        impl Notification for ServerStatusNotification {
667            type Params = ();
668            const METHOD: LspNotificationMethod<'_> =
669                LspNotificationMethod::new("experimental/serverStatus");
670            const MESSAGE_DIRECTION: MessageDirection = MessageDirection::ClientToServer;
671        }
672
673        let noti = json_rpc::RequestObject::from_notification::<ServerStatusNotification>(());
674        assert_eq!(
675            r#"{"jsonrpc":"2.0","method":"experimental/serverStatus"}"#,
676            serde_json::to_string(&noti).unwrap()
677        );
678
679        // Compilation checks
680        let method: &'static str = <ParentModule as Request>::METHOD.as_str();
681        assert_eq!(method, "experimental/parentModule");
682        let method = LspRequestMethod::new("asdf");
683        let method_str: &'static str = method.as_str();
684        assert_eq!(method_str, "asdf");
685        let method = LspRequestMethod::Custom("asdf");
686        let method_str: &'static str = method.as_str();
687        assert_eq!(method_str, "asdf");
688        let owned = String::from("workspace/didCreateFiles");
689        let method: LspNotificationMethod<'_> = owned.as_str().into();
690        assert_eq!(method, LspNotificationMethod::WorkspaceDidCreateFiles);
691        let owned = String::from("foo");
692        let method: LspNotificationMethod<'_> = owned.as_str().into();
693        assert_eq!(method, LspNotificationMethod::new("foo"));
694        // Copy
695        let method1 = LspRequestMethod::TextDocumentCompletion;
696        let method2 = method1;
697        assert_eq!(method1, method2);
698        // We can `match` on local vars without extra allocations! Yay!
699        #[allow(clippy::match_same_arms)]
700        match method {
701            LspNotificationMethod::TextDocumentWillSave => {}
702            LspNotificationMethod::Custom("foo") => {}
703            LspNotificationMethod::Custom(_) => {}
704            _ => {}
705        }
706    }
707
708    #[test]
709    fn request_with_partial_results() {
710        fn foo<R: RequestWithPartialResults>(
711            _params: R::Params,
712            _partial_result: R::PartialResult,
713        ) {
714            // Do nothing!
715        }
716
717        foo::<DocumentSymbolRequest>(
718            DocumentSymbolParams {
719                text_document: TextDocumentIdentifier { uri: "".into() },
720                work_done_progress_params: WorkDoneProgressParams {
721                    work_done_token: None,
722                },
723                partial_result_params: PartialResultParams {
724                    partial_result_token: None,
725                },
726            },
727            DocumentSymbolPartialResponse::SymbolInformationList(vec![SymbolInformation {
728                deprecated: None,
729                location: Location {
730                    uri: "".into(),
731                    range: Range::default(),
732                },
733                base_symbol_information: BaseSymbolInformation {
734                    name: String::new(),
735                    kind: SymbolKind::File,
736                    tags: None,
737                    container_name: None,
738                },
739            }]),
740        );
741
742        let partial_result = DocumentSymbolPartialResponse::DocumentSymbolList(Vec::new());
743        let _ = match partial_result {
744            DocumentSymbolPartialResponse::SymbolInformationList(_) => 1,
745            DocumentSymbolPartialResponse::DocumentSymbolList(_) => 2,
746            // No `null` branch
747        };
748    }
749
750    #[test]
751    #[allow(clippy::similar_names)]
752    fn semantic_tokens() {
753        let ste = SemanticTokensEdit {
754            start: 0,
755            delete_count: 1,
756            data: None,
757        };
758        let ste_ser = r#"{"start":0,"deleteCount":1}"#;
759        assert_eq!(serde_json::to_string(&ste).unwrap(), ste_ser);
760        assert_eq!(ste, serde_json::from_str(ste_ser).unwrap());
761
762        let ste = SemanticTokensEdit {
763            start: 0,
764            delete_count: 1,
765            data: None,
766        };
767        let ste_ser_fake = r#"{"start":0,"deleteCount":1,"data":null}"#;
768        assert_eq!(serde_json::to_string(&ste).unwrap(), ste_ser);
769        // Be permissive on technically incorrect deserialization.
770        assert_eq!(ste, serde_json::from_str(ste_ser_fake).unwrap());
771
772        let ste = SemanticTokensEdit {
773            start: 0,
774            delete_count: 1,
775            data: Some(vec![2, 5, 3, 4, 1, 0]),
776        };
777        let ste_ser = r#"{"start":0,"deleteCount":1,"data":[2,5,3,4,1,0]}"#;
778        assert_eq!(serde_json::to_string(&ste).unwrap(), ste_ser);
779        assert_eq!(ste, serde_json::from_str(ste_ser).unwrap());
780
781        let ste = SemanticTokensEdit {
782            start: 0,
783            delete_count: 1,
784            data: Some(Vec::new()),
785        };
786        let ste_ser = r#"{"start":0,"deleteCount":1,"data":[]}"#;
787        assert_eq!(serde_json::to_string(&ste).unwrap(), ste_ser);
788        assert_eq!(ste, serde_json::from_str(ste_ser).unwrap());
789
790        let st = SemanticTokens {
791            result_id: None,
792            data: Vec::default(),
793        };
794        let st_ser = r#"{"data":[]}"#;
795        assert_eq!(serde_json::to_string(&st).unwrap(), st_ser);
796        assert_eq!(st, serde_json::from_str(st_ser).unwrap());
797
798        let st = SemanticTokens {
799            result_id: None,
800            data: vec![
801                SemanticToken {
802                    delta_line: 2,
803                    delta_start: 5,
804                    length: 3,
805                    token_type: 0,
806                    token_modifiers_bitset: 3,
807                },
808                SemanticToken {
809                    delta_line: 0,
810                    delta_start: 5,
811                    length: 4,
812                    token_type: 1,
813                    token_modifiers_bitset: 0,
814                },
815            ],
816        };
817        let st_ser = r#"{"data":[2,5,3,0,3,0,5,4,1,0]}"#;
818        assert_eq!(serde_json::to_string(&st).unwrap(), st_ser);
819        assert_eq!(st, serde_json::from_str(st_ser).unwrap());
820
821        let stpr = SemanticTokensPartialResult {
822            data: vec![
823                SemanticToken {
824                    delta_line: 2,
825                    delta_start: 5,
826                    length: 3,
827                    token_type: 0,
828                    token_modifiers_bitset: 3,
829                },
830                SemanticToken {
831                    delta_line: 0,
832                    delta_start: 5,
833                    length: 4,
834                    token_type: 1,
835                    token_modifiers_bitset: 0,
836                },
837            ],
838        };
839        let stpr_ser = r#"{"data":[2,5,3,0,3,0,5,4,1,0]}"#;
840        assert_eq!(serde_json::to_string(&stpr).unwrap(), stpr_ser);
841        assert_eq!(stpr, serde_json::from_str(stpr_ser).unwrap());
842
843        let stpr = SemanticTokensPartialResult {
844            data: Vec::default(),
845        };
846        let stpr_ser = r#"{"data":[]}"#;
847        assert_eq!(serde_json::to_string(&stpr).unwrap(), stpr_ser);
848        assert_eq!(stpr, serde_json::from_str(stpr_ser).unwrap());
849
850        let slice: [u32; 5] = [1, 2, 3, 4, 5];
851        let token: SemanticToken = slice.into();
852        assert_eq!(
853            token,
854            SemanticToken {
855                delta_line: 1,
856                delta_start: 2,
857                length: 3,
858                token_type: 4,
859                token_modifiers_bitset: 5,
860            }
861        );
862        let slice: [u32; 5] = token.into();
863        assert_eq!(slice, [1, 2, 3, 4, 5]);
864    }
865
866    #[test]
867    fn request_macro() {
868        let req = json_rpc::RequestObject::from_request::<lsp_request!("workspace/workspaceFolders")>(
869            json_rpc::Id::Number(123),
870            (),
871        );
872
873        assert_eq!(
874            r#"{"jsonrpc":"2.0","id":123,"method":"workspace/workspaceFolders"}"#,
875            serde_json::to_string(&req).unwrap()
876        );
877    }
878
879    #[test]
880    fn notification_macro() {
881        let req = json_rpc::RequestObject::from_notification::<lsp_notification!("exit")>(());
882
883        assert_eq!(
884            r#"{"jsonrpc":"2.0","method":"exit"}"#,
885            serde_json::to_string(&req).unwrap()
886        );
887    }
888
889    #[test]
890    fn tuple_serialization() {
891        let pil = ParameterInformationLabel::Tuple((1, 2));
892        assert_eq!("[1,2]", serde_json::to_string(&pil).unwrap());
893        assert_eq!(pil, serde_json::from_str("[1,2]").unwrap());
894    }
895
896    #[test]
897    fn lsp_object_derives() {
898        // `LspObject` is `Hash`able
899        let _: HashMap<NotebookCell, i32> = HashMap::from([(
900            NotebookCell::new(NotebookCellKind::Code, Uri("foo".to_owned()), None, None),
901            123,
902        )]);
903
904        // Structure literals are `Hash`able
905        let _: HashMap<SemanticTokensRegistrationOptions, i32> =
906            HashMap::from([(SemanticTokensRegistrationOptions::default(), 123)]);
907
908        // `LspAny` is `Hash`able
909        let _: HashMap<Command, i32> = HashMap::from([(Command::default(), 123)]);
910    }
911
912    #[test]
913    fn hover_contents() {
914        let contents = Contents::MarkedStringList(vec![
915            MarkedString::String("Foo".to_owned()),
916            MarkedString::String("Bar".to_owned()),
917        ]);
918
919        // https://github.com/serde-rs/json/issues/1244
920        let ser = serde_json::to_string(&contents).unwrap();
921        assert_eq!(ser, r#"["Foo","Bar"]"#);
922        assert_eq!(contents, serde_json::from_str(&ser).unwrap());
923
924        let contents = Contents::MarkedString(MarkedString::String("Foo".to_owned()));
925
926        let ser = serde_json::to_string(&contents).unwrap();
927        assert_eq!(ser, r#""Foo""#);
928        assert_eq!(contents, serde_json::from_str(&ser).unwrap());
929
930        let contents = Contents::MarkupContent(MarkupContent {
931            kind: MarkupKind::Markdown,
932            value: "foo".to_owned(),
933        });
934
935        let ser = serde_json::to_string(&contents).unwrap();
936        assert_eq!(ser, r#"{"kind":"markdown","value":"foo"}"#);
937        assert_eq!(contents, serde_json::from_str(&ser).unwrap());
938    }
939}
940
941/// Tests for the "url" feature.
942#[cfg(test)]
943#[cfg(all(feature = "url", not(feature = "fluent-uri")))]
944mod test {
945    use crate::*;
946
947    #[test]
948    fn url_feature() {
949        let url = url::Url::parse("file://tmp/foo.txt/").unwrap();
950        let tdi = TextDocumentIdentifier { uri: url };
951        let ser = r#"{"uri":"file://tmp/foo.txt/"}"#;
952
953        assert_eq!(ser, serde_json::to_string(&tdi).unwrap());
954        assert_eq!(tdi, serde_json::from_str(ser).unwrap());
955    }
956}
957
958/// Tests for the "fluent-uri" feature.
959#[cfg(test)]
960#[cfg(all(feature = "fluent-uri", not(feature = "url")))]
961mod test {
962    use crate::*;
963
964    #[test]
965    fn url_feature() {
966        let uri = fluent_uri::Uri::try_from("file://tmp/foo.txt/".to_string()).unwrap();
967        let tdi = TextDocumentIdentifier { uri };
968        let ser = r#"{"uri":"file://tmp/foo.txt/"}"#;
969
970        assert_eq!(ser, serde_json::to_string(&tdi).unwrap());
971        assert_eq!(tdi, serde_json::from_str(ser).unwrap());
972    }
973}