use crate::application::merger::MergeStrategy;
use crate::application::projection_registry::TargetForm;
use crate::domain::error::{WireError, WireResult};
use persona_pack::PackRoot;
use std::path::PathBuf;
fn resolve_pack_root() -> WireResult<PathBuf> {
if let Ok(p) = std::env::var("PERSONA_PACK_ROOT") {
if !p.is_empty() {
return Ok(PathBuf::from(p));
}
}
let home = std::env::var("HOME")
.map_err(|_| WireError::Storage("persona-pack resolver: HOME unset".to_string()))?;
Ok(PathBuf::from(home).join("persona-pack"))
}
#[derive(Debug, Clone)]
pub struct ProjectionOverlay {
pub template: String,
pub target_form: TargetForm,
pub strategy: MergeStrategy,
}
pub fn read_projection_overlays(
persona_id: &str,
) -> WireResult<Option<std::collections::BTreeMap<String, ProjectionOverlay>>> {
let root = resolve_pack_root()?;
let pack = PackRoot::new(root);
let persona = match pack.read(persona_id) {
Ok(p) => p,
Err(_) => return Ok(None), };
let Some(persona_wire) = persona.extra.get("persona_wire") else {
return Ok(None);
};
let Some(table) = persona_wire.as_table() else {
return Ok(None);
};
let Some(projections) = table.get("projections").and_then(|v| v.as_table()) else {
return Ok(None);
};
let mut out = std::collections::BTreeMap::new();
for (axis, entry) in projections.iter() {
let Some(e) = entry.as_table() else { continue };
let Some(template) = e.get("template").and_then(|v| v.as_str()) else {
continue;
};
let target = e
.get("target")
.and_then(|v| v.as_str())
.and_then(|s| TargetForm::parse(s).ok())
.unwrap_or(TargetForm::Markdown);
let strategy = e
.get("strategy")
.and_then(|v| v.as_str())
.map(MergeStrategy::parse)
.unwrap_or(MergeStrategy::Replace);
out.insert(
axis.clone(),
ProjectionOverlay {
template: template.to_string(),
target_form: target,
strategy,
},
);
}
Ok(Some(out))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn missing_persona_returns_none_silently() {
let r = read_projection_overlays("__definitely_not_a_persona__");
assert!(matches!(r, Ok(None)));
}
}