1use crate::{
20 error::{PartialErrorResponse, RequestError},
21 get_command, Outbox,
22};
23use async_trait::async_trait;
24use debug_adapter_protocol::{
25 events::Event,
26 requests::{
27 ContinueRequestArguments, DisconnectRequestArguments, EvaluateRequestArguments,
28 InitializeRequestArguments, LaunchRequestArguments, NextRequestArguments,
29 PauseRequestArguments, Request, ScopesRequestArguments, SetBreakpointsRequestArguments,
30 StackTraceRequestArguments, StepInRequestArguments, StepOutRequestArguments,
31 TerminateRequestArguments, VariablesRequestArguments,
32 },
33 responses::{
34 ContinueResponseBody, ErrorResponse, ErrorResponseBody, EvaluateResponseBody,
35 ScopesResponseBody, SetBreakpointsResponseBody, StackTraceResponseBody, SuccessResponse,
36 ThreadsResponseBody, VariablesResponseBody,
37 },
38 types::Capabilities,
39 SequenceNumber,
40};
41use tokio::sync::mpsc::UnboundedReceiver;
42use typed_builder::TypedBuilder;
43
44pub trait DebugAdapterContext {
45 fn fire_event(&mut self, event: impl Into<Event> + Send);
46
47 fn start_cancellable_progress(
48 &mut self,
49 title: String,
50 message: Option<String>,
51 ) -> ProgressContext;
52
53 fn end_cancellable_progress(&mut self, progress_id: String, message: Option<String>);
54
55 fn shutdown(&mut self);
56}
57
58pub struct ProgressContext {
59 pub progress_id: String,
60 cancel_receiver: UnboundedReceiver<SequenceNumber>,
61 outbox: Outbox,
62}
63impl ProgressContext {
64 pub(super) fn new(
65 progress_id: String,
66 cancel_receiver: UnboundedReceiver<SequenceNumber>,
67 outbox: Outbox,
68 ) -> ProgressContext {
69 ProgressContext {
70 progress_id,
71 cancel_receiver,
72 outbox,
73 }
74 }
75
76 pub async fn next_cancel_request(&mut self) -> Option<CancelRequest> {
77 let request_id = self.cancel_receiver.recv().await?;
78 Some(CancelRequest {
79 outbox: self.outbox.clone(),
80 request_id,
81 response_sent: false,
82 })
83 }
84}
85impl Drop for ProgressContext {
86 fn drop(&mut self) {
87 while let Ok(open_request) = self.cancel_receiver.try_recv() {
88 self.outbox
89 .respond_unknown_progress(open_request, self.progress_id.to_string())
90 }
91 }
92}
93
94pub struct CancelRequest {
95 outbox: Outbox,
96 request_id: SequenceNumber,
97 response_sent: bool,
98}
99impl CancelRequest {
100 pub fn respond(mut self, response: Result<(), CancelErrorResponse>) {
101 self.respond_without_consuming(response)
102 }
103
104 fn respond_without_consuming(&mut self, response: Result<(), CancelErrorResponse>) {
105 if self.response_sent {
107 return;
108 }
109 self.response_sent = true;
110 let result = response
111 .map(|()| SuccessResponse::Cancel)
112 .map_err(Into::into);
113 self.outbox.respond(self.request_id, result);
114 }
115}
116impl Drop for CancelRequest {
117 fn drop(&mut self) {
118 self.respond_without_consuming(Ok(()))
119 }
120}
121
122#[derive(Clone, Debug, Eq, PartialEq, TypedBuilder)]
123pub struct CancelErrorResponse {
124 pub message: String,
132
133 #[builder(default)]
134 pub body: ErrorResponseBody,
135
136 #[builder(default, setter(skip))]
137 private: (),
138}
139impl From<CancelErrorResponse> for ErrorResponse {
140 fn from(value: CancelErrorResponse) -> Self {
141 ErrorResponse::builder()
142 .command("cancel".to_string())
143 .message(value.message)
144 .body(value.body)
145 .build()
146 }
147}
148
149#[async_trait]
150pub trait DebugAdapter {
151 type Message: Send + 'static;
152 type CustomError;
153
154 fn map_custom_error(e: Self::CustomError) -> RequestError<Self::CustomError> {
155 RequestError::Terminate(e)
156 }
157
158 async fn handle_other_message(
159 &mut self,
160 _message: Self::Message,
161 _context: impl DebugAdapterContext + Send,
162 ) -> Result<(), Self::CustomError> {
163 Ok(())
164 }
165
166 async fn handle_client_request(
167 &mut self,
168 request: Request,
169 context: impl DebugAdapterContext + Send,
170 ) -> Result<SuccessResponse, RequestError<Self::CustomError>> {
171 match request {
172 Request::ConfigurationDone => self
173 .configuration_done(context)
174 .await
175 .map(|()| SuccessResponse::ConfigurationDone),
176 Request::Continue(args) => self
177 .continue_(args, context)
178 .await
179 .map(SuccessResponse::Continue),
180 Request::Disconnect(args) => self
181 .disconnect(args, context)
182 .await
183 .map(|()| SuccessResponse::Disconnect),
184 Request::Evaluate(args) => self
185 .evaluate(args, context)
186 .await
187 .map(SuccessResponse::Evaluate),
188 Request::Initialize(args) => self
189 .initialize(args, context)
190 .await
191 .map(SuccessResponse::Initialize),
192 Request::Launch(args) => self
193 .launch(args, context)
194 .await
195 .map(|()| SuccessResponse::Launch),
196 Request::Next(args) => self
197 .next(args, context)
198 .await
199 .map(|()| SuccessResponse::Next),
200 Request::Pause(args) => self
201 .pause(args, context)
202 .await
203 .map(|()| SuccessResponse::Pause),
204 Request::Scopes(args) => self
205 .scopes(args, context)
206 .await
207 .map(SuccessResponse::Scopes),
208 Request::SetBreakpoints(args) => self
209 .set_breakpoints(args, context)
210 .await
211 .map(SuccessResponse::SetBreakpoints),
212 Request::StackTrace(args) => self
213 .stack_trace(args, context)
214 .await
215 .map(SuccessResponse::StackTrace),
216 Request::StepIn(args) => self
217 .step_in(args, context)
218 .await
219 .map(|()| SuccessResponse::StepIn),
220 Request::StepOut(args) => self
221 .step_out(args, context)
222 .await
223 .map(|()| SuccessResponse::StepOut),
224 Request::Terminate(args) => self
225 .terminate(args, context)
226 .await
227 .map(|()| SuccessResponse::Terminate),
228 Request::Threads => self.threads(context).await.map(SuccessResponse::Threads),
229 Request::Variables(args) => self
230 .variables(args, context)
231 .await
232 .map(SuccessResponse::Variables),
233 _ => {
234 let command = get_command(&request);
235 Err(RequestError::Respond(PartialErrorResponse::new(format!(
236 "Unsupported request {}",
237 command
238 ))))
239 }
240 }
241 }
242
243 async fn configuration_done(
244 &mut self,
245 _context: impl DebugAdapterContext + Send,
246 ) -> Result<(), RequestError<Self::CustomError>> {
247 Err(RequestError::Respond(PartialErrorResponse::new(
248 "Unsupported request 'configurationDone'".to_string(),
249 )))
250 }
251
252 async fn continue_(
253 &mut self,
254 _args: ContinueRequestArguments,
255 _context: impl DebugAdapterContext + Send,
256 ) -> Result<ContinueResponseBody, RequestError<Self::CustomError>>;
257
258 async fn disconnect(
259 &mut self,
260 _args: DisconnectRequestArguments,
261 mut context: impl DebugAdapterContext + Send,
262 ) -> Result<(), RequestError<Self::CustomError>> {
263 context.shutdown();
264 Ok(())
265 }
266
267 async fn evaluate(
268 &mut self,
269 _args: EvaluateRequestArguments,
270 _context: impl DebugAdapterContext + Send,
271 ) -> Result<EvaluateResponseBody, RequestError<Self::CustomError>> {
272 Err(RequestError::Respond(PartialErrorResponse::new(
273 "Unsupported request 'evaluate'".to_string(),
274 )))
275 }
276
277 async fn initialize(
278 &mut self,
279 _args: InitializeRequestArguments,
280 _context: impl DebugAdapterContext + Send,
281 ) -> Result<Capabilities, RequestError<Self::CustomError>> {
282 Err(RequestError::Respond(PartialErrorResponse::new(
283 "Unsupported request 'initialize'".to_string(),
284 )))
285 }
286
287 async fn launch(
288 &mut self,
289 _args: LaunchRequestArguments,
290 _context: impl DebugAdapterContext + Send,
291 ) -> Result<(), RequestError<Self::CustomError>> {
292 Err(RequestError::Respond(PartialErrorResponse::new(
293 "Unsupported request 'launch'".to_string(),
294 )))
295 }
296
297 async fn next(
298 &mut self,
299 _args: NextRequestArguments,
300 _context: impl DebugAdapterContext + Send,
301 ) -> Result<(), RequestError<Self::CustomError>> {
302 Err(RequestError::Respond(PartialErrorResponse::new(
303 "Unsupported request 'next'".to_string(),
304 )))
305 }
306
307 async fn pause(
308 &mut self,
309 _args: PauseRequestArguments,
310 _context: impl DebugAdapterContext + Send,
311 ) -> Result<(), RequestError<Self::CustomError>> {
312 Err(RequestError::Respond(PartialErrorResponse::new(
313 "Unsupported request 'pause'".to_string(),
314 )))
315 }
316
317 async fn scopes(
318 &mut self,
319 _args: ScopesRequestArguments,
320 _context: impl DebugAdapterContext + Send,
321 ) -> Result<ScopesResponseBody, RequestError<Self::CustomError>> {
322 Err(RequestError::Respond(PartialErrorResponse::new(
323 "Unsupported request 'scopes'".to_string(),
324 )))
325 }
326
327 async fn set_breakpoints(
328 &mut self,
329 _args: SetBreakpointsRequestArguments,
330 _context: impl DebugAdapterContext + Send,
331 ) -> Result<SetBreakpointsResponseBody, RequestError<Self::CustomError>> {
332 Err(RequestError::Respond(PartialErrorResponse::new(
333 "Unsupported request 'setBreakpoints'".to_string(),
334 )))
335 }
336
337 async fn stack_trace(
338 &mut self,
339 _args: StackTraceRequestArguments,
340 _context: impl DebugAdapterContext + Send,
341 ) -> Result<StackTraceResponseBody, RequestError<Self::CustomError>> {
342 Err(RequestError::Respond(PartialErrorResponse::new(
343 "Unsupported request 'stackTrace'".to_string(),
344 )))
345 }
346
347 async fn step_in(
348 &mut self,
349 _args: StepInRequestArguments,
350 _context: impl DebugAdapterContext + Send,
351 ) -> Result<(), RequestError<Self::CustomError>> {
352 Err(RequestError::Respond(PartialErrorResponse::new(
353 "Unsupported request 'stepIn'".to_string(),
354 )))
355 }
356
357 async fn step_out(
358 &mut self,
359 _args: StepOutRequestArguments,
360 _context: impl DebugAdapterContext + Send,
361 ) -> Result<(), RequestError<Self::CustomError>> {
362 Err(RequestError::Respond(PartialErrorResponse::new(
363 "Unsupported request 'stepOut'".to_string(),
364 )))
365 }
366
367 async fn terminate(
368 &mut self,
369 _args: TerminateRequestArguments,
370 _context: impl DebugAdapterContext + Send,
371 ) -> Result<(), RequestError<Self::CustomError>> {
372 Err(RequestError::Respond(PartialErrorResponse::new(
373 "Unsupported request 'terminate'".to_string(),
374 )))
375 }
376
377 async fn threads(
378 &mut self,
379 _context: impl DebugAdapterContext + Send,
380 ) -> Result<ThreadsResponseBody, RequestError<Self::CustomError>> {
381 Err(RequestError::Respond(PartialErrorResponse::new(
382 "Unsupported request 'threads'".to_string(),
383 )))
384 }
385
386 async fn variables(
387 &mut self,
388 _args: VariablesRequestArguments,
389 _context: impl DebugAdapterContext + Send,
390 ) -> Result<VariablesResponseBody, RequestError<Self::CustomError>> {
391 Err(RequestError::Respond(PartialErrorResponse::new(
392 "Unsupported request 'variables'".to_string(),
393 )))
394 }
395}