cargo.rs

 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(&launch_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        let mut test_name = None;
63
64        if launch_config
65            .args
66            .first()
67            .map_or(false, |arg| arg == "test")
68        {
69            if let Some(package_index) = launch_config
70                .args
71                .iter()
72                .position(|arg| arg == "-p" || arg == "--package")
73            {
74                test_name = launch_config
75                    .args
76                    .get(package_index + 2)
77                    .filter(|name| !name.starts_with("--"))
78                    .cloned();
79            }
80        }
81
82        launch_config.args.clear();
83        if let Some(test_name) = test_name {
84            launch_config.args.push(test_name);
85        }
86        Ok(())
87    }
88}