use std::path::{Path, PathBuf};
const IFORGOR_DIR: &str = ".iforgor";
pub fn discover_iforgor_dirs(start_dir: &Path) -> Vec<PathBuf> {
let mut dirs = Vec::new();
let mut current = start_dir.to_path_buf();
loop {
let candidate = current.join(IFORGOR_DIR);
if candidate.is_dir() {
dirs.push(candidate);
}
if !current.pop() {
break;
}
}
if let Some(home) = home::home_dir() {
let global = home.join(IFORGOR_DIR);
if global.is_dir() && !dirs.contains(&global) {
dirs.push(global);
}
}
dirs
}
pub fn find_domain_files(iforgor_dir: &Path) -> Vec<PathBuf> {
let mut files = Vec::new();
collect_toml_files(iforgor_dir, &mut files);
files.sort();
files
}
fn collect_toml_files(dir: &Path, files: &mut Vec<PathBuf>) {
let entries = match std::fs::read_dir(dir) {
Ok(e) => e,
Err(_) => return,
};
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
collect_toml_files(&path, files);
} else if path.extension().is_some_and(|ext| ext == "toml") {
if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
if !name.starts_with('.') {
files.push(path);
}
}
}
}
}
#[cfg(test)]
mod tests {
use {super::*, std::fs};
#[test]
fn discover_finds_iforgor_dirs_closest_first() {
let tmp = tempfile::tempdir().unwrap();
let root = tmp.path();
let deep = root.join("a/b");
fs::create_dir_all(deep.join(".iforgor")).unwrap();
fs::create_dir_all(root.join("a/.iforgor")).unwrap();
let dirs = discover_iforgor_dirs(&deep);
assert!(dirs.len() >= 2);
assert_eq!(dirs[0], deep.join(".iforgor"));
assert_eq!(dirs[1], root.join("a/.iforgor"));
}
#[test]
fn discover_no_iforgor_dirs() {
let tmp = tempfile::tempdir().unwrap();
let dirs = discover_iforgor_dirs(tmp.path());
for d in &dirs {
assert!(!d.starts_with(tmp.path()));
}
}
#[test]
fn find_domain_files_recursive() {
let tmp = tempfile::tempdir().unwrap();
let root = tmp.path();
fs::write(root.join("docker.toml"), "").unwrap();
fs::create_dir_all(root.join("ci")).unwrap();
fs::write(root.join("ci/deploy.toml"), "").unwrap();
let files = find_domain_files(root);
assert_eq!(files.len(), 2);
assert!(files.contains(&root.join("ci/deploy.toml")));
assert!(files.contains(&root.join("docker.toml")));
}
#[test]
fn find_domain_files_skips_dot_prefixed() {
let tmp = tempfile::tempdir().unwrap();
let root = tmp.path();
fs::write(root.join(".config.toml"), "").unwrap();
fs::write(root.join("valid.toml"), "").unwrap();
let files = find_domain_files(root);
assert_eq!(files.len(), 1);
assert_eq!(files[0], root.join("valid.toml"));
}
#[test]
fn find_domain_files_skips_non_toml() {
let tmp = tempfile::tempdir().unwrap();
let root = tmp.path();
fs::write(root.join("readme.md"), "").unwrap();
fs::write(root.join("script.sh"), "").unwrap();
fs::write(root.join("valid.toml"), "").unwrap();
let files = find_domain_files(root);
assert_eq!(files.len(), 1);
}
#[test]
fn find_domain_files_empty_dir() {
let tmp = tempfile::tempdir().unwrap();
let files = find_domain_files(tmp.path());
assert!(files.is_empty());
}
}