debug_adapter_protocol/
lib.rs

1pub mod events;
2pub mod requests;
3pub mod responses;
4pub mod types;
5
6mod utils;
7
8use events::Event;
9use requests::Request;
10use responses::Response;
11use serde::{Deserialize, Serialize};
12use std::fmt::Display;
13
14pub type SequenceNumber = u64;
15
16/// Base class of requests, responses, and events.
17#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
18pub struct ProtocolMessage {
19    /// Sequence number (also known as message ID). For protocol messages of type 'request' this ID can be used to cancel the request.
20    pub seq: SequenceNumber,
21
22    #[serde(flatten)]
23    pub content: ProtocolMessageContent,
24}
25
26impl ProtocolMessage {
27    pub fn new(seq: SequenceNumber, content: impl Into<ProtocolMessageContent>) -> ProtocolMessage {
28        ProtocolMessage {
29            seq,
30            content: content.into(),
31        }
32    }
33}
34
35impl Display for ProtocolMessage {
36    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37        let json = serde_json::to_string(&self).unwrap();
38        write!(f, "Content-Length: {}\r\n\r\n{}", json.len(), json)
39    }
40}
41
42#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
43#[serde(rename_all = "camelCase", tag = "type")]
44pub enum ProtocolMessageContent {
45    /// A client or debug adapter initiated request.
46    Request(Request),
47
48    /// Response for a request.
49    Response(Response),
50
51    /// A debug adapter initiated event.
52    Event(Event),
53}
54
55#[cfg(test)]
56mod tests {
57    use serde_json::{Map, Number, Value};
58
59    use super::*;
60    use crate::{events::*, requests::*, responses::*, types::*};
61    use std::{collections::HashMap, iter::FromIterator};
62
63    #[test]
64    fn test_deserialize_request_initialize() {
65        // given:
66        let json = r#"{
67            "command": "initialize",
68            "arguments": {
69                "clientID": "vscode",
70                "clientName": "Visual Studio Code",
71                "adapterID": "mock",
72                "pathFormat": "path",
73                "linesStartAt1": true,
74                "columnsStartAt1": true,
75                "supportsVariableType": true,
76                "supportsVariablePaging": true,
77                "supportsRunInTerminalRequest": true,
78                "locale": "de",
79                "supportsProgressReporting": true,
80                "supportsInvalidatedEvent": true
81            },
82            "type": "request",
83            "seq": 1
84        }"#;
85
86        // when:
87        let actual = serde_json::from_str::<ProtocolMessage>(&json).unwrap();
88
89        // then:
90        assert_eq!(
91            actual,
92            ProtocolMessage {
93                seq: 1,
94                content: InitializeRequestArguments::builder()
95                    .client_id(Some("vscode".to_string()))
96                    .client_name(Some("Visual Studio Code".to_string()))
97                    .adapter_id("mock".to_string())
98                    .locale(Some("de".to_string()))
99                    .lines_start_at_1(true)
100                    .columns_start_at_1(true)
101                    .path_format(PathFormat::Path)
102                    .supports_variable_type(true)
103                    .supports_variable_paging(true)
104                    .supports_run_in_terminal_request(true)
105                    .supports_memory_references(false)
106                    .supports_progress_reporting(true)
107                    .supports_invalidated_event(true)
108                    .build()
109                    .into()
110            }
111        );
112    }
113
114    #[test]
115    fn test_serialize_request_initialize() {
116        // given:
117        let under_test = ProtocolMessage {
118            seq: 1,
119            content: ProtocolMessageContent::Request(Request::Initialize(
120                InitializeRequestArguments::builder()
121                    .client_id(Some("vscode".to_string()))
122                    .client_name(Some("Visual Studio Code".to_string()))
123                    .adapter_id("mock".to_string())
124                    .locale(Some("de".to_string()))
125                    .lines_start_at_1(true)
126                    .columns_start_at_1(true)
127                    .path_format(PathFormat::Path)
128                    .supports_variable_type(true)
129                    .supports_variable_paging(true)
130                    .supports_run_in_terminal_request(true)
131                    .supports_memory_references(false)
132                    .supports_progress_reporting(true)
133                    .supports_invalidated_event(true)
134                    .build(),
135            )),
136        };
137
138        // when:
139        let actual = serde_json::to_string_pretty(&under_test).unwrap();
140
141        // then:
142        assert_eq!(
143            actual,
144            r#"{
145  "seq": 1,
146  "type": "request",
147  "command": "initialize",
148  "arguments": {
149    "clientID": "vscode",
150    "clientName": "Visual Studio Code",
151    "adapterID": "mock",
152    "locale": "de",
153    "linesStartAt1": true,
154    "columnsStartAt1": true,
155    "supportsVariableType": true,
156    "supportsVariablePaging": true,
157    "supportsRunInTerminalRequest": true,
158    "supportsProgressReporting": true,
159    "supportsInvalidatedEvent": true
160  }
161}"#
162        );
163    }
164
165    #[test]
166    fn test_deserialize_response_initialize() {
167        // given:
168        let json = r#"{
169            "seq": 1,
170            "type": "response",
171            "request_seq": 1,
172            "success": true,
173            "command": "initialize",
174            "body": {
175                "supportsConfigurationDoneRequest": true,
176                "supportsFunctionBreakpoints": true,
177                "supportsConditionalBreakpoints": true,
178                "supportsHitConditionalBreakpoints": true,
179                "supportsDataBreakpoints": true,
180                "supportsInstructionBreakpoints": true
181            }
182        }"#;
183
184        // when:
185        let actual = serde_json::from_str::<ProtocolMessage>(json).unwrap();
186
187        // then:
188        assert_eq!(
189            actual,
190            ProtocolMessage {
191                seq: 1,
192                content: ProtocolMessageContent::Response(Response {
193                    request_seq: 1,
194                    result: Ok(SuccessResponse::Initialize(
195                        Capabilities::builder()
196                            .supports_configuration_done_request(true)
197                            .supports_function_breakpoints(true)
198                            .supports_conditional_breakpoints(true)
199                            .supports_hit_conditional_breakpoints(true)
200                            .supports_data_breakpoints(true)
201                            .supports_instruction_breakpoints(true)
202                            .build()
203                    ))
204                })
205            }
206        )
207    }
208
209    #[test]
210    fn test_serialize_response_initialize() {
211        // given:
212        let under_test = ProtocolMessage {
213            seq: 1,
214            content: ProtocolMessageContent::Response(Response {
215                request_seq: 1,
216                result: Ok(SuccessResponse::Initialize(
217                    Capabilities::builder()
218                        .supports_configuration_done_request(true)
219                        .supports_function_breakpoints(true)
220                        .supports_conditional_breakpoints(true)
221                        .supports_hit_conditional_breakpoints(true)
222                        .supports_data_breakpoints(true)
223                        .supports_instruction_breakpoints(true)
224                        .build(),
225                )),
226            }),
227        };
228
229        // when:
230        let actual = serde_json::to_string_pretty(&under_test).unwrap();
231
232        // then:
233        assert_eq!(
234            actual,
235            r#"{
236  "seq": 1,
237  "type": "response",
238  "request_seq": 1,
239  "success": true,
240  "command": "initialize",
241  "body": {
242    "supportsConfigurationDoneRequest": true,
243    "supportsFunctionBreakpoints": true,
244    "supportsConditionalBreakpoints": true,
245    "supportsHitConditionalBreakpoints": true,
246    "supportsDataBreakpoints": true,
247    "supportsInstructionBreakpoints": true
248  }
249}"#
250        )
251    }
252
253    #[test]
254    fn test_deserialize_response_error() {
255        // given:
256        let json = r#"{
257            "seq": 1,
258            "type": "response",
259            "request_seq": 2,
260            "success": false,
261            "command": "initialize",
262            "message": "Something went wrong",
263            "body": {
264                "error": {
265                    "id": 3,
266                    "format": "This thing went wrong"
267                }
268            }
269        }"#;
270
271        // when:
272        let actual = serde_json::from_str::<ProtocolMessage>(json).unwrap();
273
274        // then:
275        assert_eq!(
276            actual,
277            ProtocolMessage {
278                seq: 1,
279                content: ProtocolMessageContent::Response(Response {
280                    request_seq: 2,
281                    result: Err(ErrorResponse::builder()
282                        .command("initialize".to_string())
283                        .message("Something went wrong".to_string())
284                        .body(ErrorResponseBody::new(Some(
285                            Message::builder()
286                                .id(3)
287                                .format("This thing went wrong".to_string())
288                                .variables(HashMap::new())
289                                .send_telemetry(false)
290                                .show_user(false)
291                                .url(None)
292                                .url_label(None)
293                                .build()
294                        )))
295                        .build())
296                })
297            }
298        )
299    }
300
301    #[test]
302    fn test_serialize_response_error() {
303        // given:
304        let under_test = ProtocolMessage {
305            seq: 1,
306            content: ProtocolMessageContent::Response(Response {
307                request_seq: 2,
308                result: Err(ErrorResponse::builder()
309                    .command("initialize".to_string())
310                    .message("Something went wrong".to_string())
311                    .body(ErrorResponseBody::new(Some(
312                        Message::builder()
313                            .id(3)
314                            .format("This thing went wrong".to_string())
315                            .variables(HashMap::new())
316                            .send_telemetry(false)
317                            .show_user(false)
318                            .url(None)
319                            .url_label(None)
320                            .build(),
321                    )))
322                    .build()),
323            }),
324        };
325
326        // when:
327        let actual = serde_json::to_string_pretty(&under_test).unwrap();
328
329        // then:
330        assert_eq!(
331            actual,
332            r#"{
333  "seq": 1,
334  "type": "response",
335  "request_seq": 2,
336  "success": false,
337  "command": "initialize",
338  "message": "Something went wrong",
339  "body": {
340    "error": {
341      "id": 3,
342      "format": "This thing went wrong"
343    }
344  }
345}"#
346        )
347    }
348
349    #[test]
350    fn test_deserialize_event_exited() {
351        // given:
352        let json = r#"{
353            "seq": 1,
354            "type": "event",
355            "event": "exited",
356            "body": {
357                "exitCode": 0
358            }
359        }"#;
360
361        // when:
362        let actual = serde_json::from_str::<ProtocolMessage>(json).unwrap();
363
364        // then:
365        assert_eq!(
366            actual,
367            ProtocolMessage {
368                seq: 1,
369                content: ExitedEventBody::builder().exit_code(0).build().into()
370            }
371        )
372    }
373
374    #[test]
375    fn test_serialize_event_exited() {
376        // given:
377        let under_test = ProtocolMessage {
378            seq: 1,
379            content: ExitedEventBody::builder().exit_code(0).build().into(),
380        };
381
382        // when:
383        let actual = serde_json::to_string_pretty(&under_test).unwrap();
384
385        // then:
386        assert_eq!(
387            actual,
388            r#"{
389  "seq": 1,
390  "type": "event",
391  "event": "exited",
392  "body": {
393    "exitCode": 0
394  }
395}"#
396        )
397    }
398
399    #[test]
400    fn test_deserialize_request_launch_with_additional_attributes() {
401        // given:
402        let json = r#"{
403            "command": "launch",
404            "arguments": {
405                "noDebug": true,
406                "__restart": "Some Value",
407                "bli": { "foo": "bar" },
408                "bla": 1,
409                "blub": true
410            },
411            "type": "request",
412            "seq": 1
413        }"#;
414
415        // when:
416        let actual = serde_json::from_str::<ProtocolMessage>(&json).unwrap();
417
418        // then:
419        assert_eq!(
420            actual,
421            ProtocolMessage {
422                seq: 1,
423                content: ProtocolMessageContent::Request(Request::Launch(
424                    LaunchRequestArguments::builder()
425                        .no_debug(true)
426                        .restart(Some(Value::String("Some Value".to_string())))
427                        .additional_attributes(Map::from_iter([
428                            (
429                                "bli".to_string(),
430                                Value::Object(Map::from_iter([(
431                                    "foo".to_string(),
432                                    Value::String("bar".to_string())
433                                )]))
434                            ),
435                            ("bla".to_string(), Value::Number(Number::from(1))),
436                            ("blub".to_string(), Value::Bool(true))
437                        ]))
438                        .build()
439                ))
440            }
441        );
442    }
443
444    #[test]
445    fn test_serialize_request_launch_with_additional_attributes() {
446        // given:
447        let under_test = ProtocolMessage {
448            seq: 1,
449            content: ProtocolMessageContent::Request(Request::Launch(
450                LaunchRequestArguments::builder()
451                    .no_debug(true)
452                    .restart(Some(Value::String("Some Value".to_string())))
453                    .additional_attributes(Map::from_iter([
454                        (
455                            "bli".to_string(),
456                            Value::Object(Map::from_iter([(
457                                "foo".to_string(),
458                                Value::String("bar".to_string()),
459                            )])),
460                        ),
461                        ("bla".to_string(), Value::Number(Number::from(1))),
462                        ("blub".to_string(), Value::Bool(true)),
463                    ]))
464                    .build(),
465            )),
466        };
467
468        // when:
469        let actual = serde_json::to_string_pretty(&under_test).unwrap();
470
471        // then:
472        assert_eq!(
473            actual,
474            r#"{
475  "seq": 1,
476  "type": "request",
477  "command": "launch",
478  "arguments": {
479    "noDebug": true,
480    "__restart": "Some Value",
481    "bli": {
482      "foo": "bar"
483    },
484    "bla": 1,
485    "blub": true
486  }
487}"#
488        );
489    }
490
491    #[test]
492    fn test_deserialize_request_launch_without_additional_attributes() {
493        // given:
494        let json = r#"{
495            "seq": 1,
496            "type": "request",
497            "command": "launch",
498            "arguments": {}
499        }"#;
500
501        // when:
502        let actual = serde_json::from_str::<ProtocolMessage>(&json).unwrap();
503
504        // then:
505        assert_eq!(
506            actual,
507            ProtocolMessage {
508                seq: 1,
509                content: ProtocolMessageContent::Request(Request::Launch(
510                    LaunchRequestArguments::builder().build()
511                ))
512            }
513        );
514    }
515
516    #[test]
517    fn test_serialize_request_launch_without_additional_attributes() {
518        // given:
519        let under_test = ProtocolMessage {
520            seq: 1,
521            content: ProtocolMessageContent::Request(Request::Launch(
522                LaunchRequestArguments::builder().build(),
523            )),
524        };
525
526        // when:
527        let actual = serde_json::to_string_pretty(&under_test).unwrap();
528
529        // then:
530        assert_eq!(
531            actual,
532            r#"{
533  "seq": 1,
534  "type": "request",
535  "command": "launch",
536  "arguments": {}
537}"#
538        );
539    }
540}