use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use futures::future::join_all;
use crate::analysis::findings::Finding;
use crate::analysis::result::{FailureReason, union_failures};
use crate::cli::check::input::Work;
use crate::languages;
use crate::languages::runner::{self};
use crate::languages::spec::ToolSpec;
pub async fn run(work: &Work, root: &Path) -> (Vec<Finding>, BTreeMap<PathBuf, FailureReason>) {
let mut failures: BTreeMap<PathBuf, FailureReason> = BTreeMap::new();
let tasks = plan_tasks(work);
let futures = tasks
.into_iter()
.map(|task| async move { run_one(task, root).await });
let outcomes = join_all(futures).await;
let mut findings = Vec::new();
for (outcome, files) in outcomes {
merge_outcome(outcome, files, &mut failures, &mut findings);
}
(findings, failures)
}
struct PlannedTask {
spec: &'static ToolSpec,
files: Vec<String>,
}
async fn run_one(task: PlannedTask, root: &Path) -> (runner::ToolOutcome, Vec<String>) {
let outcome = runner::run_tool(task.spec, root, &task.files).await;
(outcome, task.files)
}
fn plan_tasks(work: &Work) -> Vec<PlannedTask> {
let paths: Vec<&Path> = work
.by_file
.iter()
.filter_map(|hunks| hunks.first())
.map(|hunk| hunk.file_path.as_path())
.chain(work.lint_only.iter().map(PathBuf::as_path))
.collect();
languages::group_by_language(&paths)
.into_iter()
.flat_map(|(language, files)| {
let files: Vec<String> = files
.into_iter()
.map(|p| p.to_string_lossy().into_owned())
.collect();
language.tools.iter().map(move |spec| PlannedTask {
spec,
files: files.clone(),
})
})
.collect()
}
fn merge_outcome(
outcome: runner::ToolOutcome,
files: Vec<String>,
failures: &mut BTreeMap<PathBuf, FailureReason>,
findings: &mut Vec<Finding>,
) {
match outcome.status {
runner::ToolStatus::Ok => {
findings.extend(outcome.findings);
}
runner::ToolStatus::Skipped => {
}
runner::ToolStatus::Unavailable => {
let reason = FailureReason::ToolUnavailable {
tool: outcome.tool.to_owned(),
detail: outcome.detail,
};
let batch: BTreeMap<PathBuf, FailureReason> = files
.into_iter()
.map(|file| (PathBuf::from(file), reason.clone()))
.collect();
union_failures(failures, batch);
}
}
}