1use super::DapLocator;
2use anyhow::{Result, anyhow};
3use async_trait::async_trait;
4use dap::DebugAdapterConfig;
5use serde_json::Value;
6use smol::{
7 io::AsyncReadExt,
8 process::{Command, Stdio},
9};
10
11pub(super) struct CargoLocator {}
12
13#[async_trait]
14impl DapLocator for CargoLocator {
15 async fn run_locator(&self, debug_config: &mut DebugAdapterConfig) -> Result<()> {
16 let Some(launch_config) = (match &mut debug_config.request {
17 task::DebugRequestDisposition::UserConfigured(task::DebugRequestType::Launch(
18 launch_config,
19 )) => Some(launch_config),
20 _ => None,
21 }) else {
22 return Err(anyhow!("Couldn't get launch config in locator"));
23 };
24
25 let Some(cwd) = launch_config.cwd.clone() else {
26 return Err(anyhow!(
27 "Couldn't get cwd from debug config which is needed for locators"
28 ));
29 };
30
31 let mut child = Command::new("cargo")
32 .args(&debug_config.args)
33 .arg("--message-format=json")
34 .current_dir(cwd)
35 .stdout(Stdio::piped())
36 .spawn()?;
37
38 let mut output = String::new();
39 if let Some(mut stdout) = child.stdout.take() {
40 stdout.read_to_string(&mut output).await?;
41 }
42
43 let status = child.status().await?;
44 if !status.success() {
45 return Err(anyhow::anyhow!("Cargo command failed"));
46 }
47
48 let Some(executable) = output
49 .lines()
50 .filter(|line| !line.trim().is_empty())
51 .filter_map(|line| serde_json::from_str(line).ok())
52 .find_map(|json: Value| {
53 json.get("executable")
54 .and_then(Value::as_str)
55 .map(String::from)
56 })
57 else {
58 return Err(anyhow!("Couldn't get executable in cargo locator"));
59 };
60
61 launch_config.program = executable;
62 debug_config.args.clear();
63 Ok(())
64 }
65}