use crate::core::parser;
use std::path::Path;
pub(crate) fn cmd_config_merge(
file_a: &Path,
file_b: &Path,
output: Option<&Path>,
allow_collisions: bool,
) -> Result<(), String> {
let mut config_a = parser::parse_and_validate(file_a)?;
let config_b = parser::parse_and_validate(file_b)?;
let machine_collisions: Vec<_> = config_b
.machines
.keys()
.filter(|k| config_a.machines.contains_key(*k))
.cloned()
.collect();
if !machine_collisions.is_empty() && !allow_collisions {
return Err(format!(
"machine name collision(s): {} — use --allow-collisions to override",
machine_collisions.join(", ")
));
}
let resource_collisions: Vec<_> = config_b
.resources
.keys()
.filter(|k| config_a.resources.contains_key(*k))
.cloned()
.collect();
if !resource_collisions.is_empty() && !allow_collisions {
return Err(format!(
"resource ID collision(s): {} — use --allow-collisions to override",
resource_collisions.join(", ")
));
}
for (k, v) in config_b.machines {
config_a.machines.insert(k, v);
}
for (k, v) in config_b.resources {
config_a.resources.insert(k, v);
}
for (k, v) in config_b.params {
config_a.params.entry(k).or_insert(v);
}
for (k, v) in config_b.outputs {
config_a.outputs.entry(k).or_insert(v);
}
config_a.name = format!("{} + {}", config_a.name, config_b.name);
let yaml =
serde_yaml_ng::to_string(&config_a).map_err(|e| format!("serialize merged config: {e}"))?;
match output {
Some(path) => {
std::fs::write(path, &yaml).map_err(|e| format!("write {}: {e}", path.display()))?;
eprintln!("Merged config written to {}", path.display());
}
None => print!("{yaml}"),
}
let total_machines = config_a.machines.len();
let total_resources = config_a.resources.len();
eprintln!(
"Merged: {} machines, {} resources ({} machine collision(s), {} resource collision(s))",
total_machines,
total_resources,
machine_collisions.len(),
resource_collisions.len()
);
Ok(())
}