use std::collections::BTreeMap;
use crate::application::merger::MergeStrategy;
use crate::application::projection_registry::TargetForm;
use crate::domain::error::WireResult;
#[derive(Debug, Clone)]
pub struct ProjectionOverlay {
pub template: String,
pub target_form: TargetForm,
pub strategy: MergeStrategy,
}
pub fn parse_overlay_response(
value: &serde_json::Value,
) -> WireResult<BTreeMap<String, ProjectionOverlay>> {
let Some(projections) = value.get("projections").and_then(|v| v.as_object()) else {
return Ok(BTreeMap::new());
};
let mut out = BTreeMap::new();
for (slot, entry) in projections.iter() {
let Some(obj) = entry.as_object() else {
continue;
};
let Some(template) = obj.get("template").and_then(|v| v.as_str()) else {
continue;
};
let target_form = obj
.get("target_form")
.and_then(|v| v.as_str())
.and_then(|s| TargetForm::parse(s).ok())
.unwrap_or(TargetForm::Markdown);
let strategy = obj
.get("merge_strategy")
.and_then(|v| v.as_str())
.map(MergeStrategy::parse)
.unwrap_or(MergeStrategy::Replace);
out.insert(
slot.clone(),
ProjectionOverlay {
template: template.to_string(),
target_form,
strategy,
},
);
}
Ok(out)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_projections_returns_empty_map() {
let v = serde_json::json!({"projections": {}});
let m = parse_overlay_response(&v).unwrap();
assert!(m.is_empty());
}
#[test]
fn missing_projections_key_returns_empty_map() {
let v = serde_json::json!({"scheme": "persona-pack"});
let m = parse_overlay_response(&v).unwrap();
assert!(m.is_empty());
}
#[test]
fn parses_full_entry() {
let v = serde_json::json!({
"projections": {
"active": {
"template": "T",
"target_form": "markdown",
"merge_strategy": "append"
}
}
});
let m = parse_overlay_response(&v).unwrap();
assert_eq!(m.len(), 1);
let o = m.get("active").unwrap();
assert_eq!(o.template, "T");
assert!(matches!(o.target_form, TargetForm::Markdown));
assert!(matches!(o.strategy, MergeStrategy::Append));
}
#[test]
fn missing_optional_fields_default() {
let v = serde_json::json!({
"projections": {
"bio": { "template": "B" }
}
});
let m = parse_overlay_response(&v).unwrap();
let o = m.get("bio").unwrap();
assert!(matches!(o.target_form, TargetForm::Markdown));
assert!(matches!(o.strategy, MergeStrategy::Replace));
}
#[test]
fn entry_without_template_skipped() {
let v = serde_json::json!({
"projections": {
"x": { "target_form": "markdown" }
}
});
let m = parse_overlay_response(&v).unwrap();
assert!(m.is_empty());
}
}