use std::path::Path;
use sha2::Digest as _;
use crate::cache_paths;
#[cfg(test)]
use crate::constants::LINTS_CACHE_DIR;
use crate::constants::LINTS_HISTORY_JSONL;
use crate::constants::LINTS_LATEST_JSON;
use crate::project::AbsolutePath;
pub fn cache_root() -> AbsolutePath { cache_paths::lint_runs_root() }
#[cfg(test)]
pub(super) fn assert_not_default_user_cache_root(cache_root: &Path) {
let default_lint_root = cache_paths::default_app_cache_root().join(LINTS_CACHE_DIR);
assert_ne!(
cache_root,
default_lint_root.as_path(),
"tests must write lint artifacts under a temp cache root, not {}",
default_lint_root.display(),
);
}
pub fn project_key(project_root: &Path) -> String {
let path_str = project_root.to_string_lossy();
let name = project_root
.file_name()
.and_then(|name| name.to_str())
.unwrap_or("project");
let digest = sha2::Sha256::digest(path_str.as_bytes());
let hex = digest
.iter()
.take(8)
.fold(String::with_capacity(16), |mut acc, b| {
use std::fmt::Write as _;
let _ = write!(acc, "{b:02x}");
acc
});
format!("{name}-{hex}")
}
pub fn sanitize_run_id(id: &str) -> String {
id.chars()
.map(|c| {
if c.is_ascii_alphanumeric() || matches!(c, '-' | '.' | '_') {
c
} else {
'-'
}
})
.collect()
}
pub fn project_dir(project_root: &Path) -> AbsolutePath {
cache_root().join(project_key(project_root)).into()
}
pub fn project_dir_under(cache_root: &Path, project_root: &Path) -> AbsolutePath {
cache_root.join(project_key(project_root)).into()
}
pub fn output_dir_under(cache_root: &Path, project_root: &Path) -> AbsolutePath {
project_dir_under(cache_root, project_root)
}
pub fn latest_path_under(cache_root: &Path, project_root: &Path) -> AbsolutePath {
project_dir_under(cache_root, project_root)
.join(LINTS_LATEST_JSON)
.into()
}
pub fn history_path_under(cache_root: &Path, project_root: &Path) -> AbsolutePath {
project_dir_under(cache_root, project_root)
.join(LINTS_HISTORY_JSONL)
.into()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sanitize_run_id_replaces_path_illegal_chars() {
assert_eq!(
sanitize_run_id("2026-05-25T17:20:44.592-04:00"),
"2026-05-25T17-20-44.592-04-00"
);
assert_eq!(sanitize_run_id("run-abc"), "run-abc");
}
#[test]
fn project_key_matches_shasum_cli() {
let key = project_key(Path::new("/Users/natemccoy/rust/cargo-mend"));
assert_eq!(key, "cargo-mend-c76947976a369618");
}
}