use crate::template::Theme;
use serde_json::Value;
pub fn merge_themes(base: &Theme, override_theme: &Theme) -> Theme {
let mut merged = base.clone();
merged.name = override_theme.name.clone();
merged.display_name = override_theme.display_name.clone();
merged.description = override_theme.description.clone();
for (key, value) in &override_theme.tokens {
merged.tokens.insert(key.clone(), value.clone());
}
merged.host_config = deep_merge_json(&base.host_config, &override_theme.host_config);
merged
}
fn deep_merge_json(base: &Value, overlay: &Value) -> Value {
match (base, overlay) {
(Value::Object(base_map), Value::Object(overlay_map)) => {
let mut merged = base_map.clone();
for (key, value) in overlay_map {
merged
.entry(key.clone())
.and_modify(|existing| *existing = deep_merge_json(existing, value))
.or_insert_with(|| value.clone());
}
Value::Object(merged)
}
(_, overlay) if !overlay.is_null() => overlay.clone(),
(base, _) => base.clone(),
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::BTreeMap;
fn theme(name: &str, tokens: &[(&str, Value)]) -> Theme {
let mut map = BTreeMap::new();
for (k, v) in tokens {
map.insert(k.to_string(), v.clone());
}
Theme {
name: name.to_string(),
display_name: name.to_string(),
description: String::new(),
tokens: map,
host_config: Value::Null,
}
}
#[test]
fn test_merge_tokens_override_wins() {
let base = theme("base", &[("color", Value::from("#000"))]);
let overlay = theme("overlay", &[("color", Value::from("#fff"))]);
let merged = merge_themes(&base, &overlay);
assert_eq!(merged.tokens["color"], "#fff");
}
#[test]
fn test_merge_keeps_base_tokens_not_in_overlay() {
let base = theme(
"base",
&[("color", Value::from("#000")), ("spacing", Value::from(12))],
);
let overlay = theme("overlay", &[("color", Value::from("#fff"))]);
let merged = merge_themes(&base, &overlay);
assert_eq!(merged.tokens["color"], "#fff");
assert_eq!(merged.tokens["spacing"], 12);
}
#[test]
fn test_merge_preserves_overlay_name() {
let base = theme("base", &[]);
let overlay = theme("corporate", &[]);
let merged = merge_themes(&base, &overlay);
assert_eq!(merged.name, "corporate");
}
}