use std::ffi::OsString;
use std::path::Path;
use serde_json::Value;
use crate::semgrep::SemgrepArgs;
pub fn run_and_persist(
output_dir: &Path,
sg_args: &SemgrepArgs,
db_path: &Path,
abort: Option<&std::sync::atomic::AtomicBool>,
) -> anyhow::Result<Value> {
#[cfg(not(feature = "mcp"))]
let _ = abort;
let hint_args = crate::semgrep::compose_config_args(sg_args)
.map_err(|e| anyhow::anyhow!("semgrep arg composition: {e}"))?;
if !which_in_path("semgrep") {
let cmd_hint = format!(
"semgrep {} {}/",
hint_args.join(" "),
output_dir.display()
);
return Ok(serde_json::json!({"ran": false, "command": cmd_hint}));
}
let mut argv: Vec<OsString> = crate::semgrep::compose_argv(sg_args)
.map_err(|e| anyhow::anyhow!("semgrep arg composition: {e}"))?;
argv.push(OsString::from(format!("{}/", output_dir.display())));
argv.push(OsString::from("--json"));
argv.push(OsString::from("--quiet"));
#[cfg(feature = "mcp")]
let result = {
let timeout_secs = crate::mcp::subprocess::subprocess_timeout_secs();
crate::mcp::subprocess::run_command_with_timeout(
"semgrep",
&argv,
"semgrep",
timeout_secs,
abort,
)
.map_err(|e| anyhow::anyhow!("{}", e.into_mcp_error().message))
};
#[cfg(not(feature = "mcp"))]
let result = std::process::Command::new("semgrep")
.args(&argv)
.output()
.map_err(anyhow::Error::new);
match result {
Ok(out) => {
let stdout_str = String::from_utf8_lossy(&out.stdout);
crate::commands::ensure_findings_db_schema(db_path)?;
let results_persisted =
crate::commands::write_semgrep_db(&stdout_str, db_path).unwrap_or(0);
let high_severity: usize = serde_json::from_str::<Value>(&stdout_str)
.ok()
.and_then(|v| {
v.get("results").and_then(Value::as_array).map(|a| {
a.iter()
.filter(|r| {
r.get("extra")
.and_then(|e| e.get("severity"))
.and_then(Value::as_str)
== Some("ERROR")
})
.count()
})
})
.unwrap_or(0);
Ok(serde_json::json!({
"ran": true,
"results_persisted": results_persisted,
"high_severity": high_severity,
"db_table": "semgrep_results",
}))
}
Err(e) => Ok(serde_json::json!({"ran": false, "error": e.to_string()})),
}
}
fn which_in_path(name: &str) -> bool {
std::env::var_os("PATH")
.map(|path| std::env::split_paths(&path).any(|dir| dir.join(name).is_file()))
.unwrap_or(false)
}