use std::path::{Path, PathBuf};
use crate::project_root::{project_slug_at, read_project_pin, PERSONAL_PALACE, PIN_FILE_REL};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PalaceAuditStatus {
Ok,
Orphaned,
Empty,
}
#[derive(Debug, Clone)]
pub struct PalaceAuditEntry {
pub id: String,
pub data_dir: PathBuf,
pub status: PalaceAuditStatus,
}
pub fn audit_palaces(registry_dir: &Path) -> Vec<PalaceAuditEntry> {
let Ok(entries) = std::fs::read_dir(registry_dir) else {
return Vec::new();
};
let mut out = Vec::new();
for entry in entries.flatten() {
let path = entry.path();
if !path.is_dir() {
continue;
}
let id = match path.file_name().and_then(|n| n.to_str()) {
Some(s) => s.to_string(),
None => continue,
};
if !path.join("palace.json").exists() {
out.push(PalaceAuditEntry {
id,
data_dir: path,
status: PalaceAuditStatus::Empty,
});
continue;
}
if id == PERSONAL_PALACE {
out.push(PalaceAuditEntry {
id,
data_dir: path,
status: PalaceAuditStatus::Ok,
});
continue;
}
let matches_ancestor = project_slug_at(registry_dir)
.map(|slug| slug == id)
.unwrap_or(false);
if matches_ancestor {
out.push(PalaceAuditEntry {
id,
data_dir: path,
status: PalaceAuditStatus::Ok,
});
continue;
}
let home = dirs::home_dir();
let found_on_disk = home
.as_ref()
.map(|home| {
let candidates = [
home.join("Projects").join(&id),
home.join("Developer").join(&id),
home.join("Code").join(&id),
home.join(&id),
];
candidates.iter().any(|c| c.is_dir())
})
.unwrap_or(false);
if found_on_disk {
out.push(PalaceAuditEntry {
id,
data_dir: path,
status: PalaceAuditStatus::Ok,
});
continue;
}
let claimed_by_pin = home
.as_ref()
.map(|home| {
scan_project_dirs_for_pin(
&[
home.join("Projects"),
home.join("Developer"),
home.join("Code"),
home.clone(),
],
&id,
)
})
.unwrap_or(false);
let status = if claimed_by_pin {
PalaceAuditStatus::Ok
} else {
PalaceAuditStatus::Orphaned
};
out.push(PalaceAuditEntry {
id,
data_dir: path,
status,
});
}
out.sort_by(|a, b| a.id.cmp(&b.id));
out
}
pub fn scan_project_dirs_for_pin(search_dirs: &[PathBuf], palace_id: &str) -> bool {
for search_dir in search_dirs {
let Ok(entries) = std::fs::read_dir(search_dir) else {
continue;
};
for entry in entries.flatten() {
let candidate = entry.path();
if !candidate.is_dir() {
continue;
}
if let Ok(Some(pin)) = read_project_pin(&candidate) {
if pin.palace == palace_id {
tracing::debug!(
palace_id = %palace_id,
pin_path = %candidate.join(PIN_FILE_REL).display(),
"audit: palace claimed by pin file — classifying as Ok"
);
return true;
}
}
}
}
false
}