use std::fmt::Write as _;
use std::path::{Path, PathBuf};
use sha2::{Digest, Sha256};
pub fn project_id(project_root: &str) -> String {
let digest = Sha256::digest(project_root.as_bytes());
let mut hex = String::with_capacity(12);
for byte in digest.iter().take(6) {
let _ = write!(hex, "{byte:02x}");
}
hex
}
pub fn project_db_path(kindling_home: &Path, project_root: &str) -> PathBuf {
kindling_home
.join("projects")
.join(project_id(project_root))
.join("kindling.db")
}
pub fn default_kindling_home() -> Option<PathBuf> {
let home = std::env::var_os("HOME")
.filter(|v| !v.is_empty())
.or_else(|| std::env::var_os("USERPROFILE").filter(|v| !v.is_empty()))?;
Some(PathBuf::from(home).join(".kindling"))
}
pub fn resolve_db_path(project_root: &str) -> Option<PathBuf> {
if let Some(explicit) = std::env::var_os("KINDLING_DB_PATH").filter(|v| !v.is_empty()) {
return Some(PathBuf::from(explicit));
}
default_kindling_home().map(|home| project_db_path(&home, project_root))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn project_id_matches_node_crypto() {
assert_eq!(project_id("/tmp/example"), NODE_HASH_TMP_EXAMPLE);
}
#[test]
fn project_id_is_stable_and_short() {
let id = project_id("/home/user/project");
assert_eq!(id.len(), 12);
assert_eq!(id, project_id("/home/user/project"));
assert_ne!(id, project_id("/home/user/other"));
}
#[test]
fn db_path_layout() {
let path = project_db_path(Path::new("/home/u/.kindling"), "/tmp/example");
let expected = format!("/home/u/.kindling/projects/{NODE_HASH_TMP_EXAMPLE}/kindling.db");
assert_eq!(path, PathBuf::from(expected));
}
const NODE_HASH_TMP_EXAMPLE: &str = "f33aa9244af5";
}