use super::paths;
use serde::Deserialize;
use std::path::{Path, PathBuf};
#[derive(Deserialize)]
struct Roots {
#[serde(default)]
root: Vec<Entry>,
#[serde(default)]
prefix: std::collections::BTreeMap<String, String>,
}
#[derive(Deserialize)]
struct Entry {
repo: String,
spec: String,
#[serde(default)]
public: Option<String>,
}
pub struct Mapping {
pub spec: PathBuf,
pub public: Option<String>,
}
pub fn file() -> Option<PathBuf> {
if let Some(p) = std::env::var_os("SPECFOLD_ROOTS") {
return Some(PathBuf::from(p));
}
let base = std::env::var_os("XDG_CONFIG_HOME")
.map(PathBuf::from)
.or_else(|| paths::home_dir().map(|h| h.join(".config")))?;
Some(base.join("specfold/roots.toml"))
}
fn read() -> Option<Roots> {
toml::from_str(&std::fs::read_to_string(file()?).ok()?).ok()
}
pub fn prefixes() -> Vec<(String, PathBuf)> {
read()
.map(|r| r.prefix.into_iter().map(|(k, v)| (k, paths::expand(Path::new("/"), &v))).collect())
.unwrap_or_default()
}
pub fn mapped(repo: &Path) -> Option<PathBuf> {
mapping(repo).map(|m| m.spec)
}
pub fn mapping(repo: &Path) -> Option<Mapping> {
let roots = read()?;
let canon = |p: &Path| p.canonicalize().ok();
let me = canon(repo)?;
roots.root.into_iter().find_map(|e| {
let r = paths::expand(Path::new("/"), &e.repo);
(canon(&r)? == me).then(|| Mapping { spec: paths::expand(Path::new("/"), &e.spec), public: e.public })
})
}