#![cfg_attr(coverage_nightly, coverage(off))]
use super::deny_refresh::run_with_timeout;
use super::types::CachedMetric;
use std::path::Path;
use std::process::Command;
use std::time::Duration;
pub(crate) enum LintRefresh {
Recorded(Box<CachedMetric>),
Failed(String),
}
pub(crate) const LINT_STATUS_PATH: &str = ".pmat-metrics/lint-status.json";
const LINT_TIMEOUT: Duration = Duration::from_secs(900);
pub(crate) fn count_lint_errors(output: &str) -> u64 {
output
.lines()
.filter(|l| {
let l = l.trim_start();
l.starts_with("error[") || l.starts_with("error:")
})
.count() as u64
}
pub(crate) fn refresh_lint_cache(project_path: &Path) -> LintRefresh {
let output = match run_with_timeout(
Command::new("cargo")
.args([
"clippy",
"--all-targets",
"--",
"-D",
"warnings",
"-A",
"unused-variables",
])
.current_dir(project_path),
LINT_TIMEOUT,
) {
Ok(Some(output)) => output,
Ok(None) => {
return LintRefresh::Failed(format!(
"'cargo clippy --all-targets' did not finish within {}s",
LINT_TIMEOUT.as_secs()
))
}
Err(e) => return LintRefresh::Failed(format!("could not run 'cargo clippy': {e}")),
};
let stderr = String::from_utf8_lossy(&output.stderr);
let passed = output.status.success();
let value = serde_json::json!({
"passed": passed,
"error_count": count_lint_errors(&stderr),
"timestamp": chrono::Utc::now().to_rfc3339(),
"source": "pmat work complete (cargo clippy --all-targets)",
});
if let Err(e) = super::deny_refresh::write_json_status(project_path, LINT_STATUS_PATH, &value) {
return LintRefresh::Failed(format!("could not write {LINT_STATUS_PATH}: {e}"));
}
LintRefresh::Recorded(Box::new(CachedMetric {
value,
age_minutes: 0,
is_stale_warn: false,
is_stale_block: false,
}))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn thiserror_is_not_a_lint_error() {
let out = " Compiling thiserror v2.0.17\n\
Compiling pmat v3.25.0\n\
Finished `dev` profile\n";
assert_eq!(count_lint_errors(out), 0);
}
#[test]
fn real_diagnostics_are_counted() {
let out = "error[E0432]: unresolved import\n\
warning: unused variable\n\
error: aborting due to 1 previous error\n";
assert_eq!(count_lint_errors(out), 2);
}
#[test]
fn indented_diagnostics_are_counted() {
assert_eq!(count_lint_errors(" error[E0001]: boom\n"), 1);
}
#[test]
fn clean_output_counts_zero() {
assert_eq!(count_lint_errors(""), 0);
assert_eq!(count_lint_errors("Finished `dev` profile\n"), 0);
}
}