1use std::{
2 path::{Path, PathBuf},
3 str::FromStr,
4 sync::Arc,
5};
6
7use anyhow::{Context, Result};
8use async_trait::async_trait;
9use dap::{
10 StartDebuggingRequestArgumentsRequest,
11 adapters::{
12 DapDelegate, DebugAdapter, DebugAdapterBinary, DebugAdapterName, DebugTaskDefinition,
13 },
14};
15use extension::{Extension, WorktreeDelegate};
16use gpui::AsyncApp;
17use task::{DebugScenario, ZedDebugConfig};
18
19pub(crate) struct ExtensionDapAdapter {
20 extension: Arc<dyn Extension>,
21 debug_adapter_name: Arc<str>,
22 schema: serde_json::Value,
23}
24
25impl ExtensionDapAdapter {
26 pub(crate) fn new(
27 extension: Arc<dyn extension::Extension>,
28 debug_adapter_name: Arc<str>,
29 schema_path: &Path,
30 ) -> Result<Self> {
31 let schema = std::fs::read_to_string(&schema_path).with_context(|| {
32 format!(
33 "Failed to read debug adapter schema for {debug_adapter_name} (from path: `{schema_path:?}`)"
34 )
35 })?;
36 let schema = serde_json::Value::from_str(&schema).with_context(|| {
37 format!("Debug adapter schema for {debug_adapter_name} is not a valid JSON")
38 })?;
39 Ok(Self {
40 extension,
41 debug_adapter_name,
42 schema,
43 })
44 }
45}
46
47/// An adapter that allows an [`dap::adapters::DapDelegate`] to be used as a [`WorktreeDelegate`].
48struct WorktreeDelegateAdapter(pub Arc<dyn DapDelegate>);
49
50#[async_trait]
51impl WorktreeDelegate for WorktreeDelegateAdapter {
52 fn id(&self) -> u64 {
53 self.0.worktree_id().to_proto()
54 }
55
56 fn root_path(&self) -> String {
57 self.0.worktree_root_path().to_string_lossy().to_string()
58 }
59
60 async fn read_text_file(&self, path: PathBuf) -> Result<String> {
61 self.0.read_text_file(path).await
62 }
63
64 async fn which(&self, binary_name: String) -> Option<String> {
65 self.0
66 .which(binary_name.as_ref())
67 .await
68 .map(|path| path.to_string_lossy().to_string())
69 }
70
71 async fn shell_env(&self) -> Vec<(String, String)> {
72 self.0.shell_env().await.into_iter().collect()
73 }
74}
75
76#[async_trait(?Send)]
77impl DebugAdapter for ExtensionDapAdapter {
78 fn name(&self) -> DebugAdapterName {
79 self.debug_adapter_name.as_ref().into()
80 }
81
82 fn dap_schema(&self) -> serde_json::Value {
83 self.schema.clone()
84 }
85
86 async fn get_binary(
87 &self,
88 delegate: &Arc<dyn DapDelegate>,
89 config: &DebugTaskDefinition,
90 user_installed_path: Option<PathBuf>,
91 // TODO support user args in the extension API
92 _user_args: Option<Vec<String>>,
93 _cx: &mut AsyncApp,
94 ) -> Result<DebugAdapterBinary> {
95 self.extension
96 .get_dap_binary(
97 self.debug_adapter_name.clone(),
98 config.clone(),
99 user_installed_path,
100 Arc::new(WorktreeDelegateAdapter(delegate.clone())),
101 )
102 .await
103 }
104
105 async fn config_from_zed_format(&self, zed_scenario: ZedDebugConfig) -> Result<DebugScenario> {
106 self.extension.dap_config_to_scenario(zed_scenario).await
107 }
108
109 async fn request_kind(
110 &self,
111 config: &serde_json::Value,
112 ) -> Result<StartDebuggingRequestArgumentsRequest> {
113 self.extension
114 .dap_request_kind(self.debug_adapter_name.clone(), config.clone())
115 .await
116 }
117}