use derive_more::Display;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::errors::{ProfileError, Result};
use crate::merge::Driver;
use crate::name::ProfileName;
use crate::resolve::Layer;
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
pub struct MergeSpec {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub on_conflict: Option<OnConflict>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[schemars(with = "Option<String>")]
pub driver: Option<Driver>,
}
#[derive(
Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Display,
)]
#[serde(rename_all = "kebab-case")]
#[display(rename_all = "kebab-case")]
pub enum OnConflict {
#[default]
ApplyOthers,
ApplyNone,
}
#[derive(Clone, Debug, Default)]
pub struct Settings {
pub on_conflict: OnConflict,
pub driver: Driver,
}
#[derive(Clone, Debug)]
pub struct Suggestion {
pub layer: ProfileName,
pub driver: Driver,
}
impl Settings {
pub(crate) fn compose(layers: &[Layer], target: &MergeSpec) -> Result<Self> {
let on_conflict = match target.on_conflict {
Some(value) => value,
None => {
agreed(layers, "merge.on-conflict", |merge| merge.on_conflict)?.unwrap_or_default()
},
};
let driver = target.driver.clone().unwrap_or_default();
Ok(Self { on_conflict, driver })
}
pub(crate) fn suggestions(layers: &[Layer], target: &MergeSpec) -> Vec<Suggestion> {
if target.driver.is_some() {
return Vec::new();
}
layers
.iter()
.filter_map(|layer| {
let driver = layer.merge().driver.clone()?;
Some(Suggestion { layer: layer.name().clone(), driver })
})
.collect()
}
}
fn agreed<T: Copy + PartialEq>(
layers: &[Layer], key: &'static str, get: fn(&MergeSpec) -> Option<T>,
) -> Result<Option<T>> {
let mut agreed: Option<(T, &Layer)> = None;
for layer in layers {
let Some(value) = get(layer.merge()) else {
continue;
};
match agreed {
Some((first, by)) if first != value => {
let (first, second) = (by.name().clone(), layer.name().clone());
return Err(ProfileError::Setting { key: key.to_owned(), first, second }.into());
},
Some(_) => {},
None => agreed = Some((value, layer)),
}
}
Ok(agreed.map(|(value, _)| value))
}