1use serde::{Deserialize, Serialize};
7use serde_json::Value;
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
13#[serde(tag = "type", rename_all = "lowercase")]
14pub enum ProtocolMessage {
15 Request(RequestMessage),
16 Response(ResponseMessage),
17 Event(EventMessage),
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct RequestMessage {
23 pub seq: i64,
24 #[serde(rename = "type")]
25 pub message_type: String,
26 pub command: String,
27 #[serde(skip_serializing_if = "Option::is_none")]
28 pub arguments: Option<Value>,
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
33pub struct ResponseMessage {
34 pub seq: i64,
35 #[serde(rename = "type")]
36 pub message_type: String,
37 pub request_seq: i64,
38 pub success: bool,
39 pub command: String,
40 #[serde(skip_serializing_if = "Option::is_none")]
41 pub message: Option<String>,
42 #[serde(skip_serializing_if = "Option::is_none")]
43 pub body: Option<Value>,
44}
45
46#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct EventMessage {
49 pub seq: i64,
50 #[serde(rename = "type")]
51 pub message_type: String,
52 pub event: String,
53 #[serde(skip_serializing_if = "Option::is_none")]
54 pub body: Option<Value>,
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize)]
61#[serde(rename_all = "camelCase")]
62pub struct InitializeArguments {
63 #[serde(skip_serializing_if = "Option::is_none")]
64 pub client_id: Option<String>,
65 #[serde(skip_serializing_if = "Option::is_none")]
66 pub client_name: Option<String>,
67 #[serde(rename = "adapterID")]
68 pub adapter_id: String,
69 #[serde(skip_serializing_if = "Option::is_none")]
70 pub locale: Option<String>,
71 #[serde(default = "default_true")]
72 pub lines_start_at1: bool,
73 #[serde(default = "default_true")]
74 pub columns_start_at1: bool,
75 #[serde(skip_serializing_if = "Option::is_none")]
76 pub path_format: Option<String>,
77 #[serde(default)]
78 pub supports_variable_type: bool,
79 #[serde(default)]
80 pub supports_variable_paging: bool,
81 #[serde(default)]
82 pub supports_run_in_terminal_request: bool,
83 #[serde(default)]
84 pub supports_memory_references: bool,
85 #[serde(default)]
86 pub supports_progress_reporting: bool,
87}
88
89fn default_true() -> bool {
90 true
91}
92
93impl Default for InitializeArguments {
94 fn default() -> Self {
95 Self {
96 client_id: Some("debugger-cli".to_string()),
97 client_name: Some("LLM Debugger CLI".to_string()),
98 adapter_id: "lldb-dap".to_string(),
99 locale: None,
100 lines_start_at1: true,
101 columns_start_at1: true,
102 path_format: Some("path".to_string()),
103 supports_variable_type: true,
104 supports_variable_paging: true,
105 supports_run_in_terminal_request: false,
106 supports_memory_references: true,
107 supports_progress_reporting: false,
108 }
109 }
110}
111
112#[derive(Debug, Clone, Serialize, Deserialize)]
117#[serde(rename_all = "camelCase")]
118pub struct LaunchArguments {
119 pub program: String,
120 #[serde(default, skip_serializing_if = "Vec::is_empty")]
121 pub args: Vec<String>,
122 #[serde(skip_serializing_if = "Option::is_none")]
123 pub cwd: Option<String>,
124 #[serde(skip_serializing_if = "Option::is_none")]
125 pub env: Option<std::collections::HashMap<String, String>>,
126 #[serde(default)]
127 pub stop_on_entry: bool,
128
129 #[serde(skip_serializing_if = "Option::is_none")]
131 pub init_commands: Option<Vec<String>>,
132 #[serde(skip_serializing_if = "Option::is_none")]
133 pub pre_run_commands: Option<Vec<String>>,
134
135 #[serde(skip_serializing_if = "Option::is_none")]
138 pub request: Option<String>,
139 #[serde(skip_serializing_if = "Option::is_none")]
141 pub console: Option<String>,
142 #[serde(skip_serializing_if = "Option::is_none")]
144 pub python: Option<String>,
145 #[serde(skip_serializing_if = "Option::is_none")]
147 pub just_my_code: Option<bool>,
148
149 #[serde(skip_serializing_if = "Option::is_none")]
152 pub mode: Option<String>,
153 #[serde(skip_serializing_if = "Option::is_none")]
155 pub stop_at_entry: Option<bool>,
156
157 #[serde(skip_serializing_if = "Option::is_none")]
160 pub stop_at_beginning_of_main_subprogram: Option<bool>,
161
162 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
165 pub type_attr: Option<String>,
166 #[serde(skip_serializing_if = "Option::is_none")]
168 pub source_maps: Option<bool>,
169 #[serde(skip_serializing_if = "Option::is_none")]
171 pub out_files: Option<Vec<String>>,
172 #[serde(skip_serializing_if = "Option::is_none")]
174 pub runtime_executable: Option<String>,
175 #[serde(skip_serializing_if = "Option::is_none")]
177 pub runtime_args: Option<Vec<String>>,
178 #[serde(skip_serializing_if = "Option::is_none")]
180 pub skip_files: Option<Vec<String>>,
181}
182
183#[derive(Debug, Clone, Serialize, Deserialize)]
185#[serde(rename_all = "camelCase")]
186pub struct AttachArguments {
187 pub pid: u32,
188 #[serde(skip_serializing_if = "Option::is_none")]
190 pub wait_for: Option<bool>,
191}
192
193#[derive(Debug, Clone, Serialize, Deserialize)]
195#[serde(rename_all = "camelCase")]
196pub struct SetBreakpointsArguments {
197 pub source: Source,
198 #[serde(default, skip_serializing_if = "Vec::is_empty")]
199 pub breakpoints: Vec<SourceBreakpoint>,
200}
201
202#[derive(Debug, Clone, Serialize, Deserialize)]
204#[serde(rename_all = "camelCase")]
205pub struct SetFunctionBreakpointsArguments {
206 pub breakpoints: Vec<FunctionBreakpoint>,
207}
208
209#[derive(Debug, Clone, Serialize, Deserialize)]
211#[serde(rename_all = "camelCase")]
212pub struct ContinueArguments {
213 pub thread_id: i64,
214 #[serde(default)]
215 pub single_thread: bool,
216}
217
218#[derive(Debug, Clone, Serialize, Deserialize)]
220#[serde(rename_all = "camelCase")]
221pub struct StepArguments {
222 pub thread_id: i64,
223 #[serde(skip_serializing_if = "Option::is_none")]
224 pub granularity: Option<String>,
225}
226
227#[derive(Debug, Clone, Serialize, Deserialize)]
229#[serde(rename_all = "camelCase")]
230pub struct PauseArguments {
231 pub thread_id: i64,
232}
233
234#[derive(Debug, Clone, Serialize, Deserialize)]
236#[serde(rename_all = "camelCase")]
237pub struct StackTraceArguments {
238 pub thread_id: i64,
239 #[serde(skip_serializing_if = "Option::is_none")]
240 pub start_frame: Option<i64>,
241 #[serde(skip_serializing_if = "Option::is_none")]
242 pub levels: Option<i64>,
243}
244
245#[derive(Debug, Clone, Serialize, Deserialize)]
247#[serde(rename_all = "camelCase")]
248pub struct ScopesArguments {
249 pub frame_id: i64,
250}
251
252#[derive(Debug, Clone, Serialize, Deserialize)]
254#[serde(rename_all = "camelCase")]
255pub struct VariablesArguments {
256 pub variables_reference: i64,
257 #[serde(skip_serializing_if = "Option::is_none")]
258 pub start: Option<i64>,
259 #[serde(skip_serializing_if = "Option::is_none")]
260 pub count: Option<i64>,
261}
262
263#[derive(Debug, Clone, Serialize, Deserialize)]
265#[serde(rename_all = "camelCase")]
266pub struct EvaluateArguments {
267 pub expression: String,
268 #[serde(skip_serializing_if = "Option::is_none")]
269 pub frame_id: Option<i64>,
270 #[serde(skip_serializing_if = "Option::is_none")]
271 pub context: Option<String>,
272}
273
274#[derive(Debug, Clone, Serialize, Deserialize)]
276#[serde(rename_all = "camelCase")]
277pub struct DisconnectArguments {
278 #[serde(default)]
279 pub restart: bool,
280 #[serde(skip_serializing_if = "Option::is_none")]
281 pub terminate_debuggee: Option<bool>,
282}
283
284#[derive(Debug, Clone, Default, Serialize, Deserialize)]
288#[serde(rename_all = "camelCase")]
289pub struct Capabilities {
290 #[serde(default)]
291 pub supports_configuration_done_request: bool,
292 #[serde(default)]
293 pub supports_function_breakpoints: bool,
294 #[serde(default)]
295 pub supports_conditional_breakpoints: bool,
296 #[serde(default)]
297 pub supports_hit_conditional_breakpoints: bool,
298 #[serde(default)]
299 pub supports_evaluate_for_hovers: bool,
300 #[serde(default)]
301 pub supports_step_back: bool,
302 #[serde(default)]
303 pub supports_set_variable: bool,
304 #[serde(default)]
305 pub supports_restart_frame: bool,
306 #[serde(default)]
307 pub supports_restart_request: bool,
308 #[serde(default)]
309 pub supports_goto_targets_request: bool,
310 #[serde(default)]
311 pub supports_step_in_targets_request: bool,
312 #[serde(default)]
313 pub supports_completions_request: bool,
314 #[serde(default)]
315 pub supports_modules_request: bool,
316 #[serde(default)]
317 pub supports_data_breakpoints: bool,
318 #[serde(default)]
319 pub supports_read_memory_request: bool,
320 #[serde(default)]
321 pub supports_disassemble_request: bool,
322 #[serde(default)]
323 pub supports_terminate_request: bool,
324}
325
326#[derive(Debug, Clone, Serialize, Deserialize)]
328pub struct SetBreakpointsResponseBody {
329 pub breakpoints: Vec<Breakpoint>,
330}
331
332#[derive(Debug, Clone, Serialize, Deserialize)]
334#[serde(rename_all = "camelCase")]
335pub struct StackTraceResponseBody {
336 pub stack_frames: Vec<StackFrame>,
337 #[serde(skip_serializing_if = "Option::is_none")]
338 pub total_frames: Option<i64>,
339}
340
341#[derive(Debug, Clone, Serialize, Deserialize)]
343pub struct ThreadsResponseBody {
344 pub threads: Vec<Thread>,
345}
346
347#[derive(Debug, Clone, Serialize, Deserialize)]
349pub struct ScopesResponseBody {
350 pub scopes: Vec<Scope>,
351}
352
353#[derive(Debug, Clone, Serialize, Deserialize)]
355pub struct VariablesResponseBody {
356 pub variables: Vec<Variable>,
357}
358
359#[derive(Debug, Clone, Serialize, Deserialize)]
361#[serde(rename_all = "camelCase")]
362pub struct EvaluateResponseBody {
363 pub result: String,
364 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
365 pub type_name: Option<String>,
366 #[serde(default)]
367 pub variables_reference: i64,
368}
369
370#[derive(Debug, Clone, Serialize, Deserialize)]
372#[serde(rename_all = "camelCase")]
373pub struct ContinueResponseBody {
374 #[serde(default = "default_true")]
375 pub all_threads_continued: bool,
376}
377
378#[derive(Debug, Clone, Default, Serialize, Deserialize)]
382#[serde(rename_all = "camelCase")]
383pub struct Source {
384 #[serde(skip_serializing_if = "Option::is_none")]
385 pub name: Option<String>,
386 #[serde(skip_serializing_if = "Option::is_none")]
387 pub path: Option<String>,
388 #[serde(skip_serializing_if = "Option::is_none")]
389 pub source_reference: Option<i64>,
390}
391
392#[derive(Debug, Clone, Serialize, Deserialize)]
394#[serde(rename_all = "camelCase")]
395pub struct SourceBreakpoint {
396 pub line: u32,
397 #[serde(skip_serializing_if = "Option::is_none")]
398 pub column: Option<u32>,
399 #[serde(skip_serializing_if = "Option::is_none")]
400 pub condition: Option<String>,
401 #[serde(skip_serializing_if = "Option::is_none")]
402 pub hit_condition: Option<String>,
403 #[serde(skip_serializing_if = "Option::is_none")]
404 pub log_message: Option<String>,
405}
406
407#[derive(Debug, Clone, Serialize, Deserialize)]
409#[serde(rename_all = "camelCase")]
410pub struct FunctionBreakpoint {
411 pub name: String,
412 #[serde(skip_serializing_if = "Option::is_none")]
413 pub condition: Option<String>,
414 #[serde(skip_serializing_if = "Option::is_none")]
415 pub hit_condition: Option<String>,
416}
417
418#[derive(Debug, Clone, Serialize, Deserialize)]
420#[serde(rename_all = "camelCase")]
421pub struct Breakpoint {
422 #[serde(skip_serializing_if = "Option::is_none")]
423 pub id: Option<u32>,
424 pub verified: bool,
425 #[serde(skip_serializing_if = "Option::is_none")]
426 pub message: Option<String>,
427 #[serde(skip_serializing_if = "Option::is_none")]
428 pub source: Option<Source>,
429 #[serde(skip_serializing_if = "Option::is_none")]
430 pub line: Option<u32>,
431 #[serde(skip_serializing_if = "Option::is_none")]
432 pub column: Option<u32>,
433}
434
435#[derive(Debug, Clone, Serialize, Deserialize)]
437#[serde(rename_all = "camelCase")]
438pub struct StackFrame {
439 pub id: i64,
440 pub name: String,
441 #[serde(skip_serializing_if = "Option::is_none")]
442 pub source: Option<Source>,
443 pub line: u32,
444 pub column: u32,
445 #[serde(skip_serializing_if = "Option::is_none")]
446 pub module_id: Option<Value>,
447}
448
449#[derive(Debug, Clone, Serialize, Deserialize)]
451pub struct Thread {
452 pub id: i64,
453 pub name: String,
454}
455
456#[derive(Debug, Clone, Serialize, Deserialize)]
458#[serde(rename_all = "camelCase")]
459pub struct Scope {
460 pub name: String,
461 pub variables_reference: i64,
462 #[serde(default)]
463 pub expensive: bool,
464 #[serde(skip_serializing_if = "Option::is_none")]
465 pub source: Option<Source>,
466 #[serde(skip_serializing_if = "Option::is_none")]
467 pub line: Option<u32>,
468}
469
470#[derive(Debug, Clone, Serialize, Deserialize)]
472#[serde(rename_all = "camelCase")]
473pub struct Variable {
474 pub name: String,
475 pub value: String,
476 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
477 pub type_name: Option<String>,
478 #[serde(default)]
479 pub variables_reference: i64,
480}
481
482#[derive(Debug, Clone, Serialize, Deserialize)]
486#[serde(rename_all = "camelCase")]
487pub struct StoppedEventBody {
488 pub reason: String,
489 #[serde(skip_serializing_if = "Option::is_none")]
490 pub description: Option<String>,
491 #[serde(skip_serializing_if = "Option::is_none")]
492 pub thread_id: Option<i64>,
493 #[serde(default)]
494 pub all_threads_stopped: bool,
495 #[serde(default, skip_serializing_if = "Vec::is_empty")]
496 pub hit_breakpoint_ids: Vec<u32>,
497 #[serde(skip_serializing_if = "Option::is_none")]
498 pub text: Option<String>,
499}
500
501#[derive(Debug, Clone, Serialize, Deserialize)]
503pub struct OutputEventBody {
504 pub category: Option<String>,
505 pub output: String,
506 #[serde(skip_serializing_if = "Option::is_none")]
507 pub source: Option<Source>,
508 #[serde(skip_serializing_if = "Option::is_none")]
509 pub line: Option<u32>,
510}
511
512#[derive(Debug, Clone, Serialize, Deserialize)]
514#[serde(rename_all = "camelCase")]
515pub struct ThreadEventBody {
516 pub reason: String,
517 pub thread_id: i64,
518}
519
520#[derive(Debug, Clone, Serialize, Deserialize)]
522#[serde(rename_all = "camelCase")]
523pub struct ExitedEventBody {
524 pub exit_code: i32,
525}
526
527#[derive(Debug, Clone, Serialize, Deserialize)]
529pub struct TerminatedEventBody {
530 #[serde(default)]
531 pub restart: bool,
532}
533
534#[derive(Debug, Clone)]
538pub enum Event {
539 Initialized,
540 Stopped(StoppedEventBody),
541 Continued { thread_id: i64, all_threads_continued: bool },
542 Exited(ExitedEventBody),
543 Terminated(Option<TerminatedEventBody>),
544 Thread(ThreadEventBody),
545 Output(OutputEventBody),
546 Breakpoint { reason: String, breakpoint: Breakpoint },
547 Unknown { event: String, body: Option<Value> },
548}
549
550impl Event {
551 pub fn from_message(msg: &EventMessage) -> Self {
553 match msg.event.as_str() {
554 "initialized" => Event::Initialized,
555 "stopped" => {
556 if let Some(body) = &msg.body {
557 if let Ok(stopped) = serde_json::from_value(body.clone()) {
558 return Event::Stopped(stopped);
559 }
560 }
561 Event::Unknown {
562 event: msg.event.clone(),
563 body: msg.body.clone(),
564 }
565 }
566 "continued" => {
567 let thread_id = msg.body.as_ref()
568 .and_then(|b| b.get("threadId"))
569 .and_then(|v| v.as_i64())
570 .unwrap_or(0);
571 let all_threads_continued = msg.body.as_ref()
572 .and_then(|b| b.get("allThreadsContinued"))
573 .and_then(|v| v.as_bool())
574 .unwrap_or(true);
575 Event::Continued { thread_id, all_threads_continued }
576 }
577 "exited" => {
578 if let Some(body) = &msg.body {
579 if let Ok(exited) = serde_json::from_value(body.clone()) {
580 return Event::Exited(exited);
581 }
582 }
583 Event::Exited(ExitedEventBody { exit_code: 0 })
584 }
585 "terminated" => {
586 let body = msg.body.as_ref()
587 .and_then(|b| serde_json::from_value(b.clone()).ok());
588 Event::Terminated(body)
589 }
590 "thread" => {
591 if let Some(body) = &msg.body {
592 if let Ok(thread) = serde_json::from_value(body.clone()) {
593 return Event::Thread(thread);
594 }
595 }
596 Event::Unknown {
597 event: msg.event.clone(),
598 body: msg.body.clone(),
599 }
600 }
601 "output" => {
602 if let Some(body) = &msg.body {
603 if let Ok(output) = serde_json::from_value(body.clone()) {
604 return Event::Output(output);
605 }
606 }
607 Event::Unknown {
608 event: msg.event.clone(),
609 body: msg.body.clone(),
610 }
611 }
612 "breakpoint" => {
613 if let Some(body) = &msg.body {
614 let reason = body.get("reason")
615 .and_then(|v| v.as_str())
616 .unwrap_or("unknown")
617 .to_string();
618 if let Some(bp) = body.get("breakpoint") {
619 if let Ok(breakpoint) = serde_json::from_value(bp.clone()) {
620 return Event::Breakpoint { reason, breakpoint };
621 }
622 }
623 }
624 Event::Unknown {
625 event: msg.event.clone(),
626 body: msg.body.clone(),
627 }
628 }
629 _ => Event::Unknown {
630 event: msg.event.clone(),
631 body: msg.body.clone(),
632 },
633 }
634 }
635}