(
&'a self,
project_root: &'a Path,
scope: &'a Scope,
)
| 36 | } |
| 37 | |
| 38 | fn run<'a>( |
| 39 | &'a self, |
| 40 | project_root: &'a Path, |
| 41 | scope: &'a Scope, |
| 42 | ) -> Pin<Box<dyn Future<Output = Result<Vec<Diagnostic>>> + Send + 'a>> { |
| 43 | Box::pin(async move { |
| 44 | let target_dir = target_dir_for(project_root); |
| 45 | |
| 46 | let mut cmd = tokio::process::Command::new("cargo"); |
| 47 | cmd.arg("check") |
| 48 | .arg("--message-format=json") |
| 49 | .arg("--target-dir") |
| 50 | .arg(&target_dir) |
| 51 | .current_dir(project_root) |
| 52 | .stdin(Stdio::null()) |
| 53 | .stdout(Stdio::piped()) |
| 54 | .stderr(Stdio::null()) |
| 55 | .kill_on_drop(true); |
| 56 | |
| 57 | if let Scope::Package { name } = scope { |
| 58 | cmd.arg("-p").arg(name); |
| 59 | } |
| 60 | |
| 61 | let output = cmd.output().await.map_err(|e| TraceDecayError::Config { |
| 62 | message: format!("failed to spawn cargo: {e}"), |
| 63 | })?; |
| 64 | |
| 65 | let stdout = String::from_utf8_lossy(&output.stdout); |
| 66 | let mut diagnostics = Vec::new(); |
| 67 | for line in stdout.lines() { |
| 68 | if line.is_empty() { |
| 69 | continue; |
| 70 | } |
| 71 | let parsed: CargoLine = match serde_json::from_str(line) { |
| 72 | Ok(p) => p, |
| 73 | Err(_) => continue, |
| 74 | }; |
| 75 | if parsed.reason != "compiler-message" { |
| 76 | continue; |
| 77 | } |
| 78 | let Some(msg) = parsed.message else { continue }; |
| 79 | if !is_diagnostic_level(&msg.level) { |
| 80 | continue; |
| 81 | } |
| 82 | if msg.spans.is_empty() { |
| 83 | continue; |
| 84 | } |
| 85 | |
| 86 | let code = msg |
| 87 | .code |
| 88 | .as_ref() |
| 89 | .map(|c| c.code.clone()) |
| 90 | .unwrap_or_default(); |
| 91 | |
| 92 | for span in &msg.spans { |
| 93 | if !span.is_primary { |
| 94 | continue; |
| 95 | } |
no test coverage detected