use crate::types::ConfigValue;
pub type CustomMergeFn = fn(&ConfigValue, &ConfigValue) -> ConfigValue;
#[derive(Clone, Copy, Default)]
pub enum MergeStrategy {
#[default]
Replace,
Join { separator: &'static str },
Append,
Prepend,
JoinAppend { separator: &'static str },
DeepMerge,
Custom {
func: CustomMergeFn,
name: &'static str,
},
}
impl std::fmt::Debug for MergeStrategy {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MergeStrategy::Replace => write!(f, "Replace"),
MergeStrategy::Join { separator } => write!(f, "Join({:?})", separator),
MergeStrategy::Append => write!(f, "Append"),
MergeStrategy::Prepend => write!(f, "Prepend"),
MergeStrategy::JoinAppend { separator } => write!(f, "JoinAppend({:?})", separator),
MergeStrategy::DeepMerge => write!(f, "DeepMerge"),
MergeStrategy::Custom { name, .. } => write!(f, "Custom({:?})", name),
}
}
}
impl PartialEq for MergeStrategy {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(MergeStrategy::Replace, MergeStrategy::Replace) => true,
(MergeStrategy::Join { separator: a }, MergeStrategy::Join { separator: b }) => a == b,
(MergeStrategy::Append, MergeStrategy::Append) => true,
(MergeStrategy::Prepend, MergeStrategy::Prepend) => true,
(
MergeStrategy::JoinAppend { separator: a },
MergeStrategy::JoinAppend { separator: b },
) => a == b,
(MergeStrategy::DeepMerge, MergeStrategy::DeepMerge) => true,
(MergeStrategy::Custom { name: a, .. }, MergeStrategy::Custom { name: b, .. }) => {
a == b
}
_ => false,
}
}
}
impl Eq for MergeStrategy {}
impl MergeStrategy {
pub fn join(separator: &'static str) -> Self {
MergeStrategy::Join { separator }
}
pub fn join_append(separator: &'static str) -> Self {
MergeStrategy::JoinAppend { separator }
}
pub fn custom(name: &'static str, func: CustomMergeFn) -> Self {
MergeStrategy::Custom { func, name }
}
pub fn is_custom(&self) -> bool {
matches!(self, MergeStrategy::Custom { .. })
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_strategy() {
assert_eq!(MergeStrategy::default(), MergeStrategy::Replace);
}
#[test]
fn test_join_strategy() {
let s = MergeStrategy::join(":");
assert_eq!(s, MergeStrategy::Join { separator: ":" });
}
#[test]
fn test_custom_strategy() {
fn my_merge(low: &ConfigValue, high: &ConfigValue) -> ConfigValue {
match (low, high) {
(ConfigValue::String(l), ConfigValue::String(h)) => {
ConfigValue::String(format!("{}+{}", l, h))
}
_ => high.clone(),
}
}
let s = MergeStrategy::custom("my_merge", my_merge);
assert!(s.is_custom());
assert_eq!(
s,
MergeStrategy::Custom {
name: "my_merge",
func: my_merge,
}
);
}
#[test]
fn test_append_strategy() {
let s = MergeStrategy::Append;
assert_eq!(s, MergeStrategy::Append);
}
#[test]
fn test_prepend_strategy() {
let s = MergeStrategy::Prepend;
assert_eq!(s, MergeStrategy::Prepend);
}
#[test]
fn test_deep_merge_strategy() {
let s = MergeStrategy::DeepMerge;
assert_eq!(s, MergeStrategy::DeepMerge);
}
#[test]
fn test_join_append_constructor() {
let s = MergeStrategy::join_append(",");
assert_eq!(s, MergeStrategy::JoinAppend { separator: "," });
}
#[test]
fn test_is_custom_false_for_non_custom() {
assert!(!MergeStrategy::Replace.is_custom());
assert!(!MergeStrategy::Append.is_custom());
assert!(!MergeStrategy::Prepend.is_custom());
assert!(!MergeStrategy::DeepMerge.is_custom());
assert!(!MergeStrategy::join(":").is_custom());
assert!(!MergeStrategy::join_append(":").is_custom());
}
#[test]
fn test_debug_format_replace() {
assert_eq!(format!("{:?}", MergeStrategy::Replace), "Replace");
}
#[test]
fn test_debug_format_join() {
assert_eq!(format!("{:?}", MergeStrategy::join(":")), "Join(\":\")");
}
#[test]
fn test_debug_format_append() {
assert_eq!(format!("{:?}", MergeStrategy::Append), "Append");
}
#[test]
fn test_debug_format_prepend() {
assert_eq!(format!("{:?}", MergeStrategy::Prepend), "Prepend");
}
#[test]
fn test_debug_format_join_append() {
assert_eq!(
format!("{:?}", MergeStrategy::join_append(":")),
"JoinAppend(\":\")"
);
}
#[test]
fn test_debug_format_deep_merge() {
assert_eq!(format!("{:?}", MergeStrategy::DeepMerge), "DeepMerge");
}
#[test]
fn test_debug_format_custom() {
fn noop(_: &ConfigValue, high: &ConfigValue) -> ConfigValue {
high.clone()
}
let s = MergeStrategy::custom("noop", noop);
assert_eq!(format!("{:?}", s), "Custom(\"noop\")");
}
#[test]
fn test_partial_eq_same_variants() {
assert_eq!(MergeStrategy::Replace, MergeStrategy::Replace);
assert_eq!(MergeStrategy::join(":"), MergeStrategy::join(":"));
assert_eq!(MergeStrategy::Append, MergeStrategy::Append);
assert_eq!(MergeStrategy::Prepend, MergeStrategy::Prepend);
assert_eq!(
MergeStrategy::join_append(","),
MergeStrategy::join_append(",")
);
assert_eq!(MergeStrategy::DeepMerge, MergeStrategy::DeepMerge);
}
#[test]
fn test_partial_eq_different_separators() {
assert_ne!(MergeStrategy::join(":"), MergeStrategy::join(","));
assert_ne!(
MergeStrategy::join_append(":"),
MergeStrategy::join_append(",")
);
}
#[test]
fn test_partial_eq_different_variants() {
assert_ne!(MergeStrategy::Replace, MergeStrategy::Append);
assert_ne!(MergeStrategy::Append, MergeStrategy::Prepend);
assert_ne!(MergeStrategy::join(":"), MergeStrategy::join_append(":"));
assert_ne!(MergeStrategy::DeepMerge, MergeStrategy::Replace);
assert_ne!(
MergeStrategy::join(":"),
MergeStrategy::Custom {
name: "join",
func: |_, h| h.clone(),
}
);
}
#[test]
fn test_custom_eq_by_name_only() {
fn f1(_: &ConfigValue, h: &ConfigValue) -> ConfigValue {
h.clone()
}
fn f2(_: &ConfigValue, h: &ConfigValue) -> ConfigValue {
h.clone()
}
let s1 = MergeStrategy::custom("name", f1);
let s2 = MergeStrategy::custom("name", f2);
assert_eq!(s1, s2);
let s3 = MergeStrategy::custom("other", f1);
assert_ne!(s1, s3);
}
#[test]
fn test_clone_copy_behavior() {
let s = MergeStrategy::join(":");
let cloned = s;
assert_eq!(s, cloned);
let copied = s;
assert_eq!(s, copied);
}
}