deepmerge 0.1.0

Deep merge functionality with policy-driven merging and derive macro support
Documentation
use deepmerge::{DeepMerge, DeepMergeDefault, ComposedPolicy, DefaultPolicy};
#[cfg(feature = "std")]
use deepmerge::{vec_append_with_dedupe, vec_prepend_with_dedupe};

#[derive(Clone)]
struct DedupePolicy {
    dedupe: bool,
}

impl deepmerge::Policy for DedupePolicy {
    fn sequence_dedupe(&self) -> bool {
        self.dedupe
    }
}

#[test]
fn test_sequence_dedupe_policy_default() {
    // By default, sequence_dedupe is false
    let policy = DefaultPolicy;
    
    let mut vec1 = vec![1, 2, 2, 3];
    let vec2 = vec![3, 4, 4, 5];
    vec1.merge_with_policy(vec2, &policy);
    
    // No deduplication by default
    assert_eq!(vec1, vec![1, 2, 2, 3, 3, 4, 4, 5]);
}

#[test]
fn test_sequence_dedupe_policy_enabled() {
    // With sequence_dedupe enabled
    let policy = ComposedPolicy::new(DefaultPolicy)
        .with_sequence_dedupe(true);
    
    let mut vec1 = vec![1, 2, 2, 3];
    let vec2 = vec![3, 4, 4, 5];
    vec1.merge_with_policy(vec2, &policy);
    
    // Note: The Vec implementation can't deduplicate without explicit bounds
    // So this still won't deduplicate automatically
    assert_eq!(vec1, vec![1, 2, 2, 3, 3, 4, 4, 5]);
}

#[test]
#[cfg(feature = "std")]
fn test_explicit_dedupe_functions() {
    // Test the explicit dedupe functions that require bounds
    
    // Test append with dedupe
    let mut vec1 = vec![1, 2, 2, 3];
    let vec2 = vec![3, 4, 4, 5];
    vec_append_with_dedupe(&mut vec1, vec2);
    assert_eq!(vec1, vec![1, 2, 3, 4, 5]);
    
    // Test prepend with dedupe
    let mut vec1 = vec![1, 2, 2, 3];
    let vec2 = vec![3, 4, 4, 5];
    vec_prepend_with_dedupe(&mut vec1, vec2);
    assert_eq!(vec1, vec![3, 4, 5, 1, 2]);
}

#[test]
#[cfg(feature = "std")]
fn test_dedupe_with_strings() {
    // Test with strings
    let mut vec1 = vec!["a".to_string(), "b".to_string(), "b".to_string()];
    let vec2 = vec!["b".to_string(), "c".to_string(), "c".to_string()];
    vec_append_with_dedupe(&mut vec1, vec2);
    assert_eq!(vec1, vec!["a".to_string(), "b".to_string(), "c".to_string()]);
}

#[test]
#[cfg(feature = "std")]
fn test_dedupe_preserves_order() {
    // Ensure deduplication preserves the order of first occurrence
    let mut vec1 = vec![3, 1, 2, 1];
    let vec2 = vec![2, 4, 3, 4];
    vec_append_with_dedupe(&mut vec1, vec2);
    assert_eq!(vec1, vec![3, 1, 2, 4]);
}

#[test]
#[cfg(feature = "std")]
fn test_dedupe_empty_cases() {
    // Test with empty vecs
    let mut vec1: Vec<i32> = vec![];
    let vec2 = vec![1, 2, 2, 3];
    vec_append_with_dedupe(&mut vec1, vec2);
    assert_eq!(vec1, vec![1, 2, 3]);
    
    let mut vec1 = vec![1, 2, 2, 3];
    let vec2: Vec<i32> = vec![];
    vec_append_with_dedupe(&mut vec1, vec2);
    assert_eq!(vec1, vec![1, 2, 3]); // Still dedupes the original vec
}

// Test using the policy with derive macro
#[cfg(feature = "std")]
#[derive(deepmerge::DeepMerge, Debug, Clone, PartialEq)]
#[merge(policy(sequence_dedupe = true))]
struct ConfigWithDedupe {
    tags: Vec<String>,
    values: Vec<i32>,
}

#[test]
#[cfg(feature = "std")]
fn test_derive_with_sequence_dedupe() {
    // Note: Even with sequence_dedupe = true in the derive,
    // the Vec implementation can't automatically dedupe without bounds
    let mut config = ConfigWithDedupe {
        tags: vec!["rust".to_string(), "web".to_string(), "web".to_string()],
        values: vec![1, 2, 2, 3],
    };
    
    let update = ConfigWithDedupe {
        tags: vec!["api".to_string(), "rust".to_string()],
        values: vec![3, 4, 4],
    };
    
    config.merge(update);
    
    // Without explicit bounds, deduplication doesn't happen automatically
    assert_eq!(config.tags, vec![
        "rust".to_string(), 
        "web".to_string(), 
        "web".to_string(),
        "api".to_string(),
        "rust".to_string()
    ]);
    assert_eq!(config.values, vec![1, 2, 2, 3, 3, 4, 4]);
}

// Custom type that doesn't implement Hash but does implement Ord
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
struct OrdOnlyType {
    value: i32,
}

#[test]
fn test_dedupe_with_ord_only_types() {
    // For types that only implement Ord (not Hash), we can't dedupe efficiently
    let policy = ComposedPolicy::new(DefaultPolicy)
        .with_sequence_dedupe(true);
    
    let mut vec1 = vec![
        OrdOnlyType { value: 1 },
        OrdOnlyType { value: 2 },
        OrdOnlyType { value: 2 },
    ];
    let vec2 = vec![
        OrdOnlyType { value: 3 },
        OrdOnlyType { value: 3 },
    ];
    vec1.merge_with_policy(vec2, &policy);
    
    // Can't dedupe without Hash + Eq
    assert_eq!(vec1.len(), 5);
}