1use anyhow::{Context as _, Result};
2use client::proto::{
3 self, DapChecksum, DapChecksumAlgorithm, DapEvaluateContext, DapModule, DapScope,
4 DapScopePresentationHint, DapSource, DapSourcePresentationHint, DapStackFrame, DapVariable,
5};
6use dap_types::{OutputEventCategory, OutputEventGroup, ScopePresentationHint, Source};
7
8pub trait ProtoConversion {
9 type ProtoType;
10 type Output;
11
12 fn to_proto(self) -> Self::ProtoType;
13 fn from_proto(payload: Self::ProtoType) -> Self::Output;
14}
15
16impl<T> ProtoConversion for Vec<T>
17where
18 T: ProtoConversion<Output = T>,
19{
20 type ProtoType = Vec<T::ProtoType>;
21 type Output = Self;
22
23 fn to_proto(self) -> Self::ProtoType {
24 self.into_iter().map(|item| item.to_proto()).collect()
25 }
26
27 fn from_proto(payload: Self::ProtoType) -> Self {
28 payload
29 .into_iter()
30 .map(|item| T::from_proto(item))
31 .collect()
32 }
33}
34
35impl ProtoConversion for dap_types::Scope {
36 type ProtoType = DapScope;
37 type Output = Self;
38
39 fn to_proto(self) -> Self::ProtoType {
40 Self::ProtoType {
41 name: self.name,
42 presentation_hint: self.presentation_hint.map(|hint| hint.to_proto().into()),
43 variables_reference: self.variables_reference,
44 named_variables: self.named_variables,
45 indexed_variables: self.indexed_variables,
46 expensive: self.expensive,
47 source: self.source.map(Source::to_proto),
48 line: self.line,
49 end_line: self.end_line,
50 column: self.column,
51 end_column: self.end_column,
52 }
53 }
54
55 fn from_proto(payload: Self::ProtoType) -> Self {
56 let presentation_hint = payload
57 .presentation_hint
58 .and_then(DapScopePresentationHint::from_i32);
59 Self {
60 name: payload.name,
61 presentation_hint: presentation_hint.map(ScopePresentationHint::from_proto),
62 variables_reference: payload.variables_reference,
63 named_variables: payload.named_variables,
64 indexed_variables: payload.indexed_variables,
65 expensive: payload.expensive,
66 source: payload.source.map(dap_types::Source::from_proto),
67 line: payload.line,
68 end_line: payload.end_line,
69 column: payload.column,
70 end_column: payload.end_column,
71 }
72 }
73}
74
75impl ProtoConversion for dap_types::Variable {
76 type ProtoType = DapVariable;
77 type Output = Self;
78
79 fn to_proto(self) -> Self::ProtoType {
80 Self::ProtoType {
81 name: self.name,
82 value: self.value,
83 r#type: self.type_,
84 evaluate_name: self.evaluate_name,
85 variables_reference: self.variables_reference,
86 named_variables: self.named_variables,
87 indexed_variables: self.indexed_variables,
88 memory_reference: self.memory_reference,
89 }
90 }
91
92 fn from_proto(payload: Self::ProtoType) -> Self {
93 Self {
94 name: payload.name,
95 value: payload.value,
96 type_: payload.r#type,
97 evaluate_name: payload.evaluate_name,
98 presentation_hint: None, // TODO Debugger Collab Add this
99 variables_reference: payload.variables_reference,
100 named_variables: payload.named_variables,
101 indexed_variables: payload.indexed_variables,
102 memory_reference: payload.memory_reference,
103 declaration_location_reference: None, // TODO
104 value_location_reference: None, // TODO
105 }
106 }
107}
108
109impl ProtoConversion for dap_types::ScopePresentationHint {
110 type ProtoType = DapScopePresentationHint;
111 type Output = Self;
112
113 fn to_proto(self) -> Self::ProtoType {
114 match self {
115 Self::Locals => Self::ProtoType::Locals,
116 Self::Arguments => Self::ProtoType::Arguments,
117 Self::Registers => Self::ProtoType::Registers,
118 Self::ReturnValue => Self::ProtoType::ReturnValue,
119 Self::Unknown => Self::ProtoType::ScopeUnknown,
120 _ => unreachable!(),
121 }
122 }
123
124 fn from_proto(payload: Self::ProtoType) -> Self {
125 match payload {
126 Self::ProtoType::Locals => Self::Locals,
127 Self::ProtoType::Arguments => Self::Arguments,
128 Self::ProtoType::Registers => Self::Registers,
129 Self::ProtoType::ReturnValue => Self::ReturnValue,
130 Self::ProtoType::ScopeUnknown => Self::Unknown,
131 }
132 }
133}
134
135impl ProtoConversion for dap_types::SourcePresentationHint {
136 type ProtoType = DapSourcePresentationHint;
137 type Output = Self;
138
139 fn to_proto(self) -> Self::ProtoType {
140 match self {
141 Self::Normal => Self::ProtoType::SourceNormal,
142 Self::Emphasize => Self::ProtoType::Emphasize,
143 Self::Deemphasize => Self::ProtoType::Deemphasize,
144 Self::Unknown => Self::ProtoType::SourceUnknown,
145 }
146 }
147
148 fn from_proto(payload: Self::ProtoType) -> Self {
149 match payload {
150 Self::ProtoType::SourceNormal => Self::Normal,
151 Self::ProtoType::Emphasize => Self::Emphasize,
152 Self::ProtoType::Deemphasize => Self::Deemphasize,
153 Self::ProtoType::SourceUnknown => Self::Unknown,
154 }
155 }
156}
157
158impl ProtoConversion for dap_types::Checksum {
159 type ProtoType = DapChecksum;
160 type Output = Self;
161
162 fn to_proto(self) -> Self::ProtoType {
163 DapChecksum {
164 algorithm: self.algorithm.to_proto().into(),
165 checksum: self.checksum,
166 }
167 }
168
169 fn from_proto(payload: Self::ProtoType) -> Self {
170 Self {
171 algorithm: dap_types::ChecksumAlgorithm::from_proto(payload.algorithm()),
172 checksum: payload.checksum,
173 }
174 }
175}
176
177impl ProtoConversion for dap_types::ChecksumAlgorithm {
178 type ProtoType = DapChecksumAlgorithm;
179 type Output = Self;
180
181 fn to_proto(self) -> Self::ProtoType {
182 match self {
183 Self::Md5 => DapChecksumAlgorithm::Md5,
184 Self::Sha1 => DapChecksumAlgorithm::Sha1,
185 Self::Sha256 => DapChecksumAlgorithm::Sha256,
186 Self::Timestamp => DapChecksumAlgorithm::Timestamp,
187 }
188 }
189
190 fn from_proto(payload: Self::ProtoType) -> Self {
191 match payload {
192 Self::ProtoType::Md5 => Self::Md5,
193 Self::ProtoType::Sha1 => Self::Sha1,
194 Self::ProtoType::Sha256 => Self::Sha256,
195 Self::ProtoType::Timestamp => Self::Timestamp,
196 Self::ProtoType::ChecksumAlgorithmUnspecified => unreachable!(),
197 }
198 }
199}
200
201impl ProtoConversion for dap_types::Source {
202 type ProtoType = DapSource;
203 type Output = Self;
204
205 fn to_proto(self) -> Self::ProtoType {
206 Self::ProtoType {
207 name: self.name,
208 path: self.path,
209 source_reference: self.source_reference,
210 presentation_hint: self.presentation_hint.map(|hint| hint.to_proto().into()),
211 origin: self.origin,
212 sources: self.sources.map(|src| src.to_proto()).unwrap_or_default(),
213 adapter_data: Default::default(), // TODO Debugger Collab
214 checksums: self.checksums.map(|c| c.to_proto()).unwrap_or_default(),
215 }
216 }
217
218 fn from_proto(payload: Self::ProtoType) -> Self {
219 Self {
220 name: payload.name,
221 path: payload.path,
222 source_reference: payload.source_reference,
223 presentation_hint: payload
224 .presentation_hint
225 .and_then(DapSourcePresentationHint::from_i32)
226 .map(dap_types::SourcePresentationHint::from_proto),
227 origin: payload.origin,
228 sources: Some(Vec::<dap_types::Source>::from_proto(payload.sources)),
229 checksums: Some(Vec::<dap_types::Checksum>::from_proto(payload.checksums)),
230 adapter_data: None, // TODO Debugger Collab
231 }
232 }
233}
234
235impl ProtoConversion for dap_types::StackFrame {
236 type ProtoType = DapStackFrame;
237 type Output = Self;
238
239 fn to_proto(self) -> Self::ProtoType {
240 Self::ProtoType {
241 id: self.id,
242 name: self.name.clone(),
243 source: self.source.map(|src| src.to_proto()),
244 line: self.line,
245 column: self.column,
246 end_line: self.end_line,
247 end_column: self.end_column,
248 can_restart: self.can_restart,
249 instruction_pointer_reference: self.instruction_pointer_reference,
250 module_id: None, // TODO Debugger Collab
251 presentation_hint: None, // TODO Debugger Collab
252 }
253 }
254
255 fn from_proto(payload: Self::ProtoType) -> Self {
256 Self {
257 id: payload.id,
258 name: payload.name,
259 source: payload.source.map(dap_types::Source::from_proto),
260 line: payload.line,
261 column: payload.column,
262 end_line: payload.end_line,
263 end_column: payload.end_column,
264 can_restart: payload.can_restart,
265 instruction_pointer_reference: payload.instruction_pointer_reference,
266 module_id: None, // TODO Debugger Collab
267 presentation_hint: None, // TODO Debugger Collab
268 }
269 }
270}
271
272impl ProtoConversion for dap_types::ModuleId {
273 type ProtoType = proto::dap_module_id::Id;
274 type Output = Self;
275
276 fn to_proto(self) -> Self::ProtoType {
277 match self {
278 Self::Number(num) => Self::ProtoType::Number(num),
279 Self::String(string) => Self::ProtoType::String(string),
280 }
281 }
282
283 fn from_proto(payload: Self::ProtoType) -> Self::Output {
284 match payload {
285 Self::ProtoType::Number(num) => Self::Number(num),
286 Self::ProtoType::String(string) => Self::String(string),
287 }
288 }
289}
290
291impl ProtoConversion for dap_types::Module {
292 type ProtoType = DapModule;
293 type Output = Result<Self>;
294
295 fn to_proto(self) -> Self::ProtoType {
296 DapModule {
297 id: Some(proto::DapModuleId {
298 id: Some(self.id.to_proto()),
299 }),
300 name: self.name,
301 path: self.path,
302 is_optimized: self.is_optimized,
303 is_user_code: self.is_user_code,
304 version: self.version,
305 symbol_status: self.symbol_status,
306 symbol_file_path: self.symbol_file_path,
307 date_time_stamp: self.date_time_stamp,
308 address_range: self.address_range,
309 }
310 }
311
312 fn from_proto(payload: Self::ProtoType) -> Result<Self> {
313 let id = match payload
314 .id
315 .context("All DapModule proto messages must have an id")?
316 .id
317 .context("All DapModuleID proto messages must have an id")?
318 {
319 proto::dap_module_id::Id::String(string) => dap_types::ModuleId::String(string),
320 proto::dap_module_id::Id::Number(num) => dap_types::ModuleId::Number(num),
321 };
322
323 Ok(Self {
324 id,
325 name: payload.name,
326 path: payload.path,
327 is_optimized: payload.is_optimized,
328 is_user_code: payload.is_user_code,
329 version: payload.version,
330 symbol_status: payload.symbol_status,
331 symbol_file_path: payload.symbol_file_path,
332 date_time_stamp: payload.date_time_stamp,
333 address_range: payload.address_range,
334 })
335 }
336}
337
338impl ProtoConversion for dap_types::SteppingGranularity {
339 type ProtoType = proto::SteppingGranularity;
340 type Output = Self;
341
342 fn to_proto(self) -> Self::ProtoType {
343 match self {
344 Self::Statement => Self::ProtoType::Statement,
345 Self::Line => Self::ProtoType::Line,
346 Self::Instruction => Self::ProtoType::Instruction,
347 }
348 }
349
350 fn from_proto(payload: Self::ProtoType) -> Self {
351 match payload {
352 Self::ProtoType::Line => Self::Line,
353 Self::ProtoType::Instruction => Self::Instruction,
354 Self::ProtoType::Statement => Self::Statement,
355 }
356 }
357}
358
359impl ProtoConversion for dap_types::OutputEventCategory {
360 type ProtoType = proto::DapOutputCategory;
361 type Output = Self;
362
363 fn to_proto(self) -> Self::ProtoType {
364 match self {
365 Self::Console => Self::ProtoType::ConsoleOutput,
366 Self::Important => Self::ProtoType::Important,
367 Self::Stdout => Self::ProtoType::Stdout,
368 Self::Stderr => Self::ProtoType::Stderr,
369 _ => Self::ProtoType::Unknown,
370 }
371 }
372
373 fn from_proto(payload: Self::ProtoType) -> Self {
374 match payload {
375 Self::ProtoType::ConsoleOutput => Self::Console,
376 Self::ProtoType::Important => Self::Important,
377 Self::ProtoType::Stdout => Self::Stdout,
378 Self::ProtoType::Stderr => Self::Stderr,
379 Self::ProtoType::Unknown => Self::Unknown,
380 }
381 }
382}
383
384impl ProtoConversion for dap_types::OutputEvent {
385 type ProtoType = proto::DapOutputEvent;
386 type Output = Self;
387
388 fn to_proto(self) -> Self::ProtoType {
389 Self::ProtoType {
390 category: self.category.map(|category| category.to_proto().into()),
391 output: self.output.clone(),
392 variables_reference: self.variables_reference,
393 source: self.source.map(|source| source.to_proto()),
394 line: self.line.map(|line| line as u32),
395 column: self.column.map(|column| column as u32),
396 group: self.group.map(|group| group.to_proto().into()),
397 }
398 }
399
400 fn from_proto(payload: Self::ProtoType) -> Self {
401 Self {
402 category: payload
403 .category
404 .and_then(proto::DapOutputCategory::from_i32)
405 .map(OutputEventCategory::from_proto),
406 output: payload.output,
407 variables_reference: payload.variables_reference,
408 source: payload.source.map(Source::from_proto),
409 line: payload.line.map(|line| line as u64),
410 column: payload.column.map(|column| column as u64),
411 group: payload
412 .group
413 .and_then(proto::DapOutputEventGroup::from_i32)
414 .map(OutputEventGroup::from_proto),
415 data: None,
416 location_reference: None,
417 }
418 }
419}
420
421impl ProtoConversion for dap_types::OutputEventGroup {
422 type ProtoType = proto::DapOutputEventGroup;
423 type Output = Self;
424
425 fn to_proto(self) -> Self::ProtoType {
426 match self {
427 Self::Start => Self::ProtoType::Start,
428 Self::StartCollapsed => Self::ProtoType::StartCollapsed,
429 Self::End => Self::ProtoType::End,
430 }
431 }
432
433 fn from_proto(payload: Self::ProtoType) -> Self {
434 match payload {
435 Self::ProtoType::Start => Self::Start,
436 Self::ProtoType::StartCollapsed => Self::StartCollapsed,
437 Self::ProtoType::End => Self::End,
438 }
439 }
440}
441
442impl ProtoConversion for dap_types::CompletionItem {
443 type ProtoType = proto::DapCompletionItem;
444 type Output = Self;
445
446 fn to_proto(self) -> Self::ProtoType {
447 Self::ProtoType {
448 label: self.label.clone(),
449 text: self.text.clone(),
450 detail: self.detail.clone(),
451 typ: self
452 .type_
453 .map(ProtoConversion::to_proto)
454 .map(|typ| typ.into()),
455 start: self.start,
456 length: self.length,
457 selection_start: self.selection_start,
458 selection_length: self.selection_length,
459 sort_text: self.sort_text,
460 }
461 }
462
463 fn from_proto(payload: Self::ProtoType) -> Self {
464 let typ = payload.typ(); // todo(debugger): This might be a potential issue/bug because it defaults to a type when it's None
465
466 Self {
467 label: payload.label,
468 detail: payload.detail,
469 sort_text: payload.sort_text,
470 text: payload.text.clone(),
471 type_: Some(dap_types::CompletionItemType::from_proto(typ)),
472 start: payload.start,
473 length: payload.length,
474 selection_start: payload.selection_start,
475 selection_length: payload.selection_length,
476 }
477 }
478}
479
480impl ProtoConversion for dap_types::EvaluateArgumentsContext {
481 type ProtoType = DapEvaluateContext;
482 type Output = Self;
483
484 fn to_proto(self) -> Self::ProtoType {
485 match self {
486 Self::Variables => Self::ProtoType::EvaluateVariables,
487 Self::Watch => Self::ProtoType::Watch,
488 Self::Hover => Self::ProtoType::Hover,
489 Self::Repl => Self::ProtoType::Repl,
490 Self::Clipboard => Self::ProtoType::Clipboard,
491 Self::Unknown => Self::ProtoType::EvaluateUnknown,
492 _ => Self::ProtoType::EvaluateUnknown,
493 }
494 }
495
496 fn from_proto(payload: Self::ProtoType) -> Self {
497 match payload {
498 Self::ProtoType::EvaluateVariables => Self::Variables,
499 Self::ProtoType::Watch => Self::Watch,
500 Self::ProtoType::Hover => Self::Hover,
501 Self::ProtoType::Repl => Self::Repl,
502 Self::ProtoType::Clipboard => Self::Clipboard,
503 Self::ProtoType::EvaluateUnknown => Self::Unknown,
504 }
505 }
506}
507
508impl ProtoConversion for dap_types::CompletionItemType {
509 type ProtoType = proto::DapCompletionItemType;
510 type Output = Self;
511
512 fn to_proto(self) -> Self::ProtoType {
513 match self {
514 Self::Class => Self::ProtoType::Class,
515 Self::Color => Self::ProtoType::Color,
516 Self::Constructor => Self::ProtoType::Constructor,
517 Self::Customcolor => Self::ProtoType::Customcolor,
518 Self::Enum => Self::ProtoType::Enum,
519 Self::Field => Self::ProtoType::Field,
520 Self::File => Self::ProtoType::CompletionItemFile,
521 Self::Function => Self::ProtoType::Function,
522 Self::Interface => Self::ProtoType::Interface,
523 Self::Keyword => Self::ProtoType::Keyword,
524 Self::Method => Self::ProtoType::Method,
525 Self::Module => Self::ProtoType::Module,
526 Self::Property => Self::ProtoType::Property,
527 Self::Reference => Self::ProtoType::Reference,
528 Self::Snippet => Self::ProtoType::Snippet,
529 Self::Text => Self::ProtoType::Text,
530 Self::Unit => Self::ProtoType::Unit,
531 Self::Value => Self::ProtoType::Value,
532 Self::Variable => Self::ProtoType::Variable,
533 }
534 }
535
536 fn from_proto(payload: Self::ProtoType) -> Self {
537 match payload {
538 Self::ProtoType::Class => Self::Class,
539 Self::ProtoType::Color => Self::Color,
540 Self::ProtoType::CompletionItemFile => Self::File,
541 Self::ProtoType::Constructor => Self::Constructor,
542 Self::ProtoType::Customcolor => Self::Customcolor,
543 Self::ProtoType::Enum => Self::Enum,
544 Self::ProtoType::Field => Self::Field,
545 Self::ProtoType::Function => Self::Function,
546 Self::ProtoType::Interface => Self::Interface,
547 Self::ProtoType::Keyword => Self::Keyword,
548 Self::ProtoType::Method => Self::Method,
549 Self::ProtoType::Module => Self::Module,
550 Self::ProtoType::Property => Self::Property,
551 Self::ProtoType::Reference => Self::Reference,
552 Self::ProtoType::Snippet => Self::Snippet,
553 Self::ProtoType::Text => Self::Text,
554 Self::ProtoType::Unit => Self::Unit,
555 Self::ProtoType::Value => Self::Value,
556 Self::ProtoType::Variable => Self::Variable,
557 }
558 }
559}
560
561impl ProtoConversion for dap_types::Thread {
562 type ProtoType = proto::DapThread;
563 type Output = Self;
564
565 fn to_proto(self) -> Self::ProtoType {
566 proto::DapThread {
567 id: self.id,
568 name: self.name,
569 }
570 }
571
572 fn from_proto(payload: Self::ProtoType) -> Self {
573 Self {
574 id: payload.id,
575 name: payload.name,
576 }
577 }
578}