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
150#[derive(Debug, Clone, Serialize, Deserialize)]
152#[serde(rename_all = "camelCase")]
153pub struct AttachArguments {
154 pub pid: u32,
155 #[serde(skip_serializing_if = "Option::is_none")]
157 pub wait_for: Option<bool>,
158}
159
160#[derive(Debug, Clone, Serialize, Deserialize)]
162#[serde(rename_all = "camelCase")]
163pub struct SetBreakpointsArguments {
164 pub source: Source,
165 #[serde(default, skip_serializing_if = "Vec::is_empty")]
166 pub breakpoints: Vec<SourceBreakpoint>,
167}
168
169#[derive(Debug, Clone, Serialize, Deserialize)]
171#[serde(rename_all = "camelCase")]
172pub struct SetFunctionBreakpointsArguments {
173 pub breakpoints: Vec<FunctionBreakpoint>,
174}
175
176#[derive(Debug, Clone, Serialize, Deserialize)]
178#[serde(rename_all = "camelCase")]
179pub struct ContinueArguments {
180 pub thread_id: i64,
181 #[serde(default)]
182 pub single_thread: bool,
183}
184
185#[derive(Debug, Clone, Serialize, Deserialize)]
187#[serde(rename_all = "camelCase")]
188pub struct StepArguments {
189 pub thread_id: i64,
190 #[serde(skip_serializing_if = "Option::is_none")]
191 pub granularity: Option<String>,
192}
193
194#[derive(Debug, Clone, Serialize, Deserialize)]
196#[serde(rename_all = "camelCase")]
197pub struct PauseArguments {
198 pub thread_id: i64,
199}
200
201#[derive(Debug, Clone, Serialize, Deserialize)]
203#[serde(rename_all = "camelCase")]
204pub struct StackTraceArguments {
205 pub thread_id: i64,
206 #[serde(skip_serializing_if = "Option::is_none")]
207 pub start_frame: Option<i64>,
208 #[serde(skip_serializing_if = "Option::is_none")]
209 pub levels: Option<i64>,
210}
211
212#[derive(Debug, Clone, Serialize, Deserialize)]
214#[serde(rename_all = "camelCase")]
215pub struct ScopesArguments {
216 pub frame_id: i64,
217}
218
219#[derive(Debug, Clone, Serialize, Deserialize)]
221#[serde(rename_all = "camelCase")]
222pub struct VariablesArguments {
223 pub variables_reference: i64,
224 #[serde(skip_serializing_if = "Option::is_none")]
225 pub start: Option<i64>,
226 #[serde(skip_serializing_if = "Option::is_none")]
227 pub count: Option<i64>,
228}
229
230#[derive(Debug, Clone, Serialize, Deserialize)]
232#[serde(rename_all = "camelCase")]
233pub struct EvaluateArguments {
234 pub expression: String,
235 #[serde(skip_serializing_if = "Option::is_none")]
236 pub frame_id: Option<i64>,
237 #[serde(skip_serializing_if = "Option::is_none")]
238 pub context: Option<String>,
239}
240
241#[derive(Debug, Clone, Serialize, Deserialize)]
243#[serde(rename_all = "camelCase")]
244pub struct DisconnectArguments {
245 #[serde(default)]
246 pub restart: bool,
247 #[serde(skip_serializing_if = "Option::is_none")]
248 pub terminate_debuggee: Option<bool>,
249}
250
251#[derive(Debug, Clone, Default, Serialize, Deserialize)]
255#[serde(rename_all = "camelCase")]
256pub struct Capabilities {
257 #[serde(default)]
258 pub supports_configuration_done_request: bool,
259 #[serde(default)]
260 pub supports_function_breakpoints: bool,
261 #[serde(default)]
262 pub supports_conditional_breakpoints: bool,
263 #[serde(default)]
264 pub supports_hit_conditional_breakpoints: bool,
265 #[serde(default)]
266 pub supports_evaluate_for_hovers: bool,
267 #[serde(default)]
268 pub supports_step_back: bool,
269 #[serde(default)]
270 pub supports_set_variable: bool,
271 #[serde(default)]
272 pub supports_restart_frame: bool,
273 #[serde(default)]
274 pub supports_restart_request: bool,
275 #[serde(default)]
276 pub supports_goto_targets_request: bool,
277 #[serde(default)]
278 pub supports_step_in_targets_request: bool,
279 #[serde(default)]
280 pub supports_completions_request: bool,
281 #[serde(default)]
282 pub supports_modules_request: bool,
283 #[serde(default)]
284 pub supports_data_breakpoints: bool,
285 #[serde(default)]
286 pub supports_read_memory_request: bool,
287 #[serde(default)]
288 pub supports_disassemble_request: bool,
289 #[serde(default)]
290 pub supports_terminate_request: bool,
291}
292
293#[derive(Debug, Clone, Serialize, Deserialize)]
295pub struct SetBreakpointsResponseBody {
296 pub breakpoints: Vec<Breakpoint>,
297}
298
299#[derive(Debug, Clone, Serialize, Deserialize)]
301#[serde(rename_all = "camelCase")]
302pub struct StackTraceResponseBody {
303 pub stack_frames: Vec<StackFrame>,
304 #[serde(skip_serializing_if = "Option::is_none")]
305 pub total_frames: Option<i64>,
306}
307
308#[derive(Debug, Clone, Serialize, Deserialize)]
310pub struct ThreadsResponseBody {
311 pub threads: Vec<Thread>,
312}
313
314#[derive(Debug, Clone, Serialize, Deserialize)]
316pub struct ScopesResponseBody {
317 pub scopes: Vec<Scope>,
318}
319
320#[derive(Debug, Clone, Serialize, Deserialize)]
322pub struct VariablesResponseBody {
323 pub variables: Vec<Variable>,
324}
325
326#[derive(Debug, Clone, Serialize, Deserialize)]
328#[serde(rename_all = "camelCase")]
329pub struct EvaluateResponseBody {
330 pub result: String,
331 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
332 pub type_name: Option<String>,
333 #[serde(default)]
334 pub variables_reference: i64,
335}
336
337#[derive(Debug, Clone, Serialize, Deserialize)]
339#[serde(rename_all = "camelCase")]
340pub struct ContinueResponseBody {
341 #[serde(default = "default_true")]
342 pub all_threads_continued: bool,
343}
344
345#[derive(Debug, Clone, Default, Serialize, Deserialize)]
349#[serde(rename_all = "camelCase")]
350pub struct Source {
351 #[serde(skip_serializing_if = "Option::is_none")]
352 pub name: Option<String>,
353 #[serde(skip_serializing_if = "Option::is_none")]
354 pub path: Option<String>,
355 #[serde(skip_serializing_if = "Option::is_none")]
356 pub source_reference: Option<i64>,
357}
358
359#[derive(Debug, Clone, Serialize, Deserialize)]
361#[serde(rename_all = "camelCase")]
362pub struct SourceBreakpoint {
363 pub line: u32,
364 #[serde(skip_serializing_if = "Option::is_none")]
365 pub column: Option<u32>,
366 #[serde(skip_serializing_if = "Option::is_none")]
367 pub condition: Option<String>,
368 #[serde(skip_serializing_if = "Option::is_none")]
369 pub hit_condition: Option<String>,
370 #[serde(skip_serializing_if = "Option::is_none")]
371 pub log_message: Option<String>,
372}
373
374#[derive(Debug, Clone, Serialize, Deserialize)]
376#[serde(rename_all = "camelCase")]
377pub struct FunctionBreakpoint {
378 pub name: String,
379 #[serde(skip_serializing_if = "Option::is_none")]
380 pub condition: Option<String>,
381 #[serde(skip_serializing_if = "Option::is_none")]
382 pub hit_condition: Option<String>,
383}
384
385#[derive(Debug, Clone, Serialize, Deserialize)]
387#[serde(rename_all = "camelCase")]
388pub struct Breakpoint {
389 #[serde(skip_serializing_if = "Option::is_none")]
390 pub id: Option<u32>,
391 pub verified: bool,
392 #[serde(skip_serializing_if = "Option::is_none")]
393 pub message: Option<String>,
394 #[serde(skip_serializing_if = "Option::is_none")]
395 pub source: Option<Source>,
396 #[serde(skip_serializing_if = "Option::is_none")]
397 pub line: Option<u32>,
398 #[serde(skip_serializing_if = "Option::is_none")]
399 pub column: Option<u32>,
400}
401
402#[derive(Debug, Clone, Serialize, Deserialize)]
404#[serde(rename_all = "camelCase")]
405pub struct StackFrame {
406 pub id: i64,
407 pub name: String,
408 #[serde(skip_serializing_if = "Option::is_none")]
409 pub source: Option<Source>,
410 pub line: u32,
411 pub column: u32,
412 #[serde(skip_serializing_if = "Option::is_none")]
413 pub module_id: Option<Value>,
414}
415
416#[derive(Debug, Clone, Serialize, Deserialize)]
418pub struct Thread {
419 pub id: i64,
420 pub name: String,
421}
422
423#[derive(Debug, Clone, Serialize, Deserialize)]
425#[serde(rename_all = "camelCase")]
426pub struct Scope {
427 pub name: String,
428 pub variables_reference: i64,
429 #[serde(default)]
430 pub expensive: bool,
431 #[serde(skip_serializing_if = "Option::is_none")]
432 pub source: Option<Source>,
433 #[serde(skip_serializing_if = "Option::is_none")]
434 pub line: Option<u32>,
435}
436
437#[derive(Debug, Clone, Serialize, Deserialize)]
439#[serde(rename_all = "camelCase")]
440pub struct Variable {
441 pub name: String,
442 pub value: String,
443 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
444 pub type_name: Option<String>,
445 #[serde(default)]
446 pub variables_reference: i64,
447}
448
449#[derive(Debug, Clone, Serialize, Deserialize)]
453#[serde(rename_all = "camelCase")]
454pub struct StoppedEventBody {
455 pub reason: String,
456 #[serde(skip_serializing_if = "Option::is_none")]
457 pub description: Option<String>,
458 #[serde(skip_serializing_if = "Option::is_none")]
459 pub thread_id: Option<i64>,
460 #[serde(default)]
461 pub all_threads_stopped: bool,
462 #[serde(default, skip_serializing_if = "Vec::is_empty")]
463 pub hit_breakpoint_ids: Vec<u32>,
464 #[serde(skip_serializing_if = "Option::is_none")]
465 pub text: Option<String>,
466}
467
468#[derive(Debug, Clone, Serialize, Deserialize)]
470pub struct OutputEventBody {
471 pub category: Option<String>,
472 pub output: String,
473 #[serde(skip_serializing_if = "Option::is_none")]
474 pub source: Option<Source>,
475 #[serde(skip_serializing_if = "Option::is_none")]
476 pub line: Option<u32>,
477}
478
479#[derive(Debug, Clone, Serialize, Deserialize)]
481#[serde(rename_all = "camelCase")]
482pub struct ThreadEventBody {
483 pub reason: String,
484 pub thread_id: i64,
485}
486
487#[derive(Debug, Clone, Serialize, Deserialize)]
489#[serde(rename_all = "camelCase")]
490pub struct ExitedEventBody {
491 pub exit_code: i32,
492}
493
494#[derive(Debug, Clone, Serialize, Deserialize)]
496pub struct TerminatedEventBody {
497 #[serde(default)]
498 pub restart: bool,
499}
500
501#[derive(Debug, Clone)]
505pub enum Event {
506 Initialized,
507 Stopped(StoppedEventBody),
508 Continued { thread_id: i64, all_threads_continued: bool },
509 Exited(ExitedEventBody),
510 Terminated(Option<TerminatedEventBody>),
511 Thread(ThreadEventBody),
512 Output(OutputEventBody),
513 Breakpoint { reason: String, breakpoint: Breakpoint },
514 Unknown { event: String, body: Option<Value> },
515}
516
517impl Event {
518 pub fn from_message(msg: &EventMessage) -> Self {
520 match msg.event.as_str() {
521 "initialized" => Event::Initialized,
522 "stopped" => {
523 if let Some(body) = &msg.body {
524 if let Ok(stopped) = serde_json::from_value(body.clone()) {
525 return Event::Stopped(stopped);
526 }
527 }
528 Event::Unknown {
529 event: msg.event.clone(),
530 body: msg.body.clone(),
531 }
532 }
533 "continued" => {
534 let thread_id = msg.body.as_ref()
535 .and_then(|b| b.get("threadId"))
536 .and_then(|v| v.as_i64())
537 .unwrap_or(0);
538 let all_threads_continued = msg.body.as_ref()
539 .and_then(|b| b.get("allThreadsContinued"))
540 .and_then(|v| v.as_bool())
541 .unwrap_or(true);
542 Event::Continued { thread_id, all_threads_continued }
543 }
544 "exited" => {
545 if let Some(body) = &msg.body {
546 if let Ok(exited) = serde_json::from_value(body.clone()) {
547 return Event::Exited(exited);
548 }
549 }
550 Event::Exited(ExitedEventBody { exit_code: 0 })
551 }
552 "terminated" => {
553 let body = msg.body.as_ref()
554 .and_then(|b| serde_json::from_value(b.clone()).ok());
555 Event::Terminated(body)
556 }
557 "thread" => {
558 if let Some(body) = &msg.body {
559 if let Ok(thread) = serde_json::from_value(body.clone()) {
560 return Event::Thread(thread);
561 }
562 }
563 Event::Unknown {
564 event: msg.event.clone(),
565 body: msg.body.clone(),
566 }
567 }
568 "output" => {
569 if let Some(body) = &msg.body {
570 if let Ok(output) = serde_json::from_value(body.clone()) {
571 return Event::Output(output);
572 }
573 }
574 Event::Unknown {
575 event: msg.event.clone(),
576 body: msg.body.clone(),
577 }
578 }
579 "breakpoint" => {
580 if let Some(body) = &msg.body {
581 let reason = body.get("reason")
582 .and_then(|v| v.as_str())
583 .unwrap_or("unknown")
584 .to_string();
585 if let Some(bp) = body.get("breakpoint") {
586 if let Ok(breakpoint) = serde_json::from_value(bp.clone()) {
587 return Event::Breakpoint { reason, breakpoint };
588 }
589 }
590 }
591 Event::Unknown {
592 event: msg.event.clone(),
593 body: msg.body.clone(),
594 }
595 }
596 _ => Event::Unknown {
597 event: msg.event.clone(),
598 body: msg.body.clone(),
599 },
600 }
601 }
602}