deepmerge 0.1.0

Deep merge functionality with policy-driven merging and derive macro support
Documentation
#![doc = include_str!("../README.md")]
//! A flexible deep merge library for Rust with policy-driven merging and derive macros featuring typed attributes.
//!
//! This crate provides comprehensive deep merge functionality with compile-time configuration
//! through derive macros, policy-driven behavior, and support for complex data structures.
//!
//! # Features
//!
//! - **Policy-driven merging**: Configure how different types should be merged
//! - **Derive macro support**: Automatically implement `DeepMerge` for your structs  
//! - **Typed attributes**: Use identifiers instead of string literals for better compile-time checking
//! - **Flexible attribute syntax**: Mix string literals, identifiers, and path expressions
//! - **Precedence rules**: Field-level > struct-level > caller-provided policies
//! - **Multiple merge strategies**: Append, prepend, union, concatenation, replacement, and more
//! - **No-std compatible**: Works without the standard library (with `alloc`)
//!
//! # Quick Start with Prelude
//!
//! For convenience, import everything you need with the prelude:
//!
//! ```rust
//! use deepmerge::prelude::*;
//!
//! #[derive(DeepMerge)]
//! struct AppConfig {
//!     name: String,
//!     port: u16,
//! }
//!
//! let mut config = AppConfig { 
//!     name: "myapp".to_string(), 
//!     port: 8080 
//! };
//!
//! let update = AppConfig { 
//!     name: "newname".to_string(), 
//!     port: 9090 
//! };
//!
//! config.merge(update);
//! assert_eq!(config.name, "newname");
//! assert_eq!(config.port, 9090);
//! ```
//!
//! # Basic Usage with Explicit Policies
//!
//! ```rust
//! use deepmerge::prelude::*;
//!
//! // Simple derive without policy attributes
//! #[derive(DeepMerge, Debug)]
//! struct Config {
//!     pub title: String,
//!     pub tags: Vec<String>,
//!     pub enabled: bool,
//!     pub version: String,
//! }
//!
//! let mut config = Config {
//!     title: "My App".to_string(),
//!     tags: vec!["web".to_string()],
//!     enabled: false,
//!     version: "1.0".to_string(),
//! };
//!
//! let update = Config {
//!     title: " v2".to_string(),
//!     tags: vec!["api".to_string()],
//!     enabled: true,
//!     version: "2.0".to_string(),
//! };
//!
//! // Use explicit policy for complex merge behavior
//! let policy = ComposedPolicy::new(DefaultPolicy)
//!     .with_string_merge(StringMerge::Concat)
//!     .with_sequence_merge(SequenceMerge::Append)
//!     .with_bool_merge(BoolMerge::TrueWins);
//!     
//! config.merge_with_policy(update, &policy);
//!
//! // Results:
//! assert_eq!(config.title, "My App v2"); // concatenated
//! assert_eq!(config.tags, vec!["web", "api"]); // appended  
//! assert_eq!(config.enabled, true); // true wins
//! assert_eq!(config.version, "2.0"); // replaced (no field-level override available yet)
//! ```
//!
//! # Policy Configuration
//!
//! Currently, policy configuration is done through explicit `ComposedPolicy` usage.
//! Derive macro policy attributes are temporarily disabled due to trait system complexity.
//!
//! ```rust
//! use deepmerge::prelude::*;
//!
//! #[derive(DeepMerge)]
//! struct FlexibleConfig {
//!     name: String,
//!     items: Vec<i32>,
//!     enabled: bool,
//!     count: i32,
//! }
//!
//! // Configure policies explicitly
//! let policy = ComposedPolicy::new(DefaultPolicy)
//!     .with_string_merge(StringMerge::Concat)
//!     .with_sequence_merge(SequenceMerge::Append) 
//!     .with_bool_merge(BoolMerge::TrueWins)
//!     .with_number_merge(NumberMerge::Sum)
//!     .with_map_merge(MapMerge::Overlay);
//!
//! let mut config = FlexibleConfig { /* ... */ };
//! let update = FlexibleConfig { /* ... */ };
//! config.merge_with_policy(update, &policy);
//! ```
//!
//! # Available Merge Policies
//!
//! - **String Policies**: `concat`, `keep`, `replace`
//! - **Sequence Policies**: `append`, `prepend`, `union`, `extend`, `intersect`  
//! - **Boolean Policies**: `true_wins`, `false_wins`, `replace`, `keep`
//! - **Number Policies**: `sum`, `max`, `min`, `replace`, `keep`
//! - **Map Policies**: `overlay`, `union`, `left`, `right`
//! - **Option Policies**: `take`, `preserve`, `or_left`
//!
//! # Using the Prelude
//!
//! The prelude module provides all commonly used items in a single import:
//!
//! ```rust
//! // Import everything you need with one line
//! use deepmerge::prelude::*;
//!
//! // Now you have access to:
//! // - DeepMerge trait and derive macro
//! // - All policy types (DefaultPolicy, ComposedPolicy)
//! // - All merge strategy enums (StringMerge, SequenceMerge, etc.)
//! // - All convenience functions (deep_merge, merged, etc.)
//! ```
//!
//! For version stability, you can also import from a specific version:
//!
//! ```rust
//! use deepmerge::prelude::v1::*;
//! ```
//!
//! # Policy Usage
//!
//! Currently, policies are configured explicitly through `ComposedPolicy`.
//! This provides fine-grained control over merge behavior.
//!
//! ```rust
//! use deepmerge::prelude::*;
//!
//! #[derive(DeepMerge, Debug)]
//! struct App {
//!     name: String,
//!     count: i32,
//! }
//!
//! let mut a = App { name: "svc".into(), count: 2 };
//! let b = App { name: "new".into(), count: 3 };
//!
//! // Configure specific merge behavior
//! let policy = ComposedPolicy::new(DefaultPolicy)
//!     .with_string_merge(StringMerge::Replace)
//!     .with_number_merge(NumberMerge::Sum);
//!
//! a.merge_with_policy(b, &policy);
//! assert_eq!(a.name, "new"); // replaced
//! assert_eq!(a.count, 5);    // summed (2 + 3)
//! ```
//!
//! ## Per-policy examples (concise)
//!
//! - String policies:
//!   - Replace (default)
//!   - Keep
//!   - Concat / `ConcatWithSep`
//! ```rust
//! use deepmerge::prelude::*;
//! #[derive(DeepMerge, Debug)]
//! struct S { s: String }
//! let mut a = S { s: "a".into() };
//! let policy = ComposedPolicy::new(DefaultPolicy).with_string_merge(StringMerge::Concat);
//! a.merge_with_policy(S { s: "b".into() }, &policy);
//! assert_eq!(a.s, "ab");
//! ```
//!
//! - Number policies: Replace (default), Keep, Sum, Max, Min
//! ```rust
//! use deepmerge::prelude::*;
//! #[derive(DeepMerge, Debug)]
//! struct N { n: i32 }
//! let mut a = N { n: 2 };
//! let policy = ComposedPolicy::new(DefaultPolicy).with_number_merge(NumberMerge::Max);
//! a.merge_with_policy(N { n: 5 }, &policy);
//! assert_eq!(a.n, 5);
//! ```
//!
//! - Bool policies: Replace (default), Keep, `TrueWins`, `FalseWins`
//! ```rust
//! use deepmerge::prelude::*;
//! #[derive(DeepMerge, Debug)]
//! struct B { b: bool }
//! let mut a = B { b: false };
//! let policy = ComposedPolicy::new(DefaultPolicy).with_bool_merge(BoolMerge::TrueWins);
//! a.merge_with_policy(B { b: true }, &policy);
//! assert!(a.b);
//! ```
//!
//! - Sequence policies: Append (default), Prepend, Extend, Union, Intersect
//! ```rust
//! use deepmerge::prelude::*;
//! #[derive(DeepMerge, Debug)]
//! struct L { v: Vec<i32> }
//! let mut a = L { v: vec![1,2] };
//! let policy = ComposedPolicy::new(DefaultPolicy).with_sequence_merge(SequenceMerge::Append);
//! a.merge_with_policy(L { v: vec![2,3] }, &policy);
//! assert_eq!(a.v, vec![1,2,2,3]);
//! ```
//!
//! - Map policies: Overlay (default), Union, Left, Right
//! ```rust
//! use std::collections::HashMap;
//! use deepmerge::prelude::*;
//! #[derive(DeepMerge, Debug)]
//! #[merge(policy(map = overlay))]
//! struct M { m: HashMap<&'static str, i32> }
//! let mut a = M { m: [("a",1)].into_iter().collect() };
//! a.merge(M { m: [("a",2),("b",3)].into_iter().collect() });
//! assert_eq!(a.m.get("a"), Some(&2));
//! assert_eq!(a.m.get("b"), Some(&3));
//! ```
//!
//! - Option policies: Take (default), Preserve, `OrLeft`
//! ```rust
//! use deepmerge::prelude::*;
//! #[derive(DeepMerge, Debug)]
//! #[merge(policy(option = preserve))]
//! struct O { o: Option<i32> }
//! let mut a = O { o: Some(1) };
//! a.merge(O { o: Some(2) });
//! assert_eq!(a.o, Some(1));
//! ```

#![forbid(unsafe_code)]
#![cfg_attr(not(feature = "std"), no_std)]
#![deny(missing_docs)]

#[cfg(feature = "alloc")]
extern crate alloc;

pub mod policy;
pub mod handlers;
pub mod forwarding;
pub mod prelude;

#[cfg(test)]
mod tests;

pub use policy::{
    Policy, DefaultPolicy, DeepMerge, DeepMergeDefault, DeepMergeFrom, DeepMergeFromDefault,
    StrictReplacePolicy, PreservePolicy, DedupeCollectionsPolicy,
    ComposedPolicy, ScalarAction, SequenceMerge, OptionMerge, NumberMerge, MapMerge, BoolMerge, StringMerge, Condition, MergeOutcome
};

// Re-export derive macro if feature is enabled
#[cfg(feature = "derive")]
pub use deepmerge_derive::DeepMerge;

// Top-level convenience functions
/// Deep merge two values using the default policy
pub fn deep_merge<T: DeepMergeDefault>(dst: &mut T, src: T) {
    dst.merge(src);
}

/// Deep merge two values using a specific policy
pub fn deep_merge_with_policy<T: DeepMerge<P>, P: Policy>(dst: &mut T, src: T, policy: &P) {
    dst.merge_with_policy(src, policy);
}

/// Deep merge by reference using the default policy
pub fn deep_merge_ref<T: DeepMergeDefault + Clone>(dst: &mut T, src: &T) {
    DeepMergeDefault::merge_ref(dst, src);
}

/// Deep merge by reference using a specific policy
pub fn deep_merge_ref_with_policy<T: DeepMerge<P> + Clone, P: Policy>(dst: &mut T, src: &T, policy: &P) {
    DeepMerge::merge_ref(dst, src, policy);
}

/// Merge two values and return a new merged value using the default policy
/// This provides parity with `deep_merge` but returns a new value instead of mutating
pub fn merged<T: DeepMergeDefault>(dst: T, src: T) -> T {
    dst.merged(src)
}

/// Merge two values and return a new merged value using a specific policy
pub fn merged_with_policy<T: DeepMerge<P>, P: Policy>(dst: T, src: T, policy: &P) -> T {
    dst.merged_with_policy(src, policy)
}

/// Deep merge with change reporting using the default policy
pub fn deep_merge_reporting<T: DeepMergeDefault>(dst: &mut T, src: T) -> MergeOutcome {
    dst.merge_reporting(src)
}

/// Deep merge with change reporting using a specific policy
pub fn deep_merge_with_policy_reporting<T: DeepMerge<P>, P: Policy>(dst: &mut T, src: T, policy: &P) -> MergeOutcome {
    dst.merge_with_policy_reporting(src, policy)
}

/// Deep merge from another type using the default policy
pub fn deep_merge_from<T: DeepMergeFromDefault<U>, U>(dst: &mut T, src: U) {
    dst.merge_from(src);
}

/// Deep merge from another type using a specific policy
pub fn deep_merge_from_with_policy<T: DeepMergeFrom<U, P>, U, P: Policy>(dst: &mut T, src: U, policy: &P) {
    dst.merge_from_with_policy(src, policy);
}

/// Deep merge from another type with change reporting using the default policy
pub fn deep_merge_from_reporting<T: DeepMergeFromDefault<U>, U>(dst: &mut T, src: U) -> MergeOutcome {
    dst.merge_from_reporting(src)
}

/// Deep merge from another type with change reporting using a specific policy
pub fn deep_merge_from_with_policy_reporting<T: DeepMergeFrom<U, P>, U, P: Policy>(dst: &mut T, src: U, policy: &P) -> MergeOutcome {
    dst.merge_from_with_policy_reporting(src, policy)
}

// Export Vec helper functions for deduplication and by-key operations
#[cfg(all(feature = "std", feature = "alloc"))]
pub use handlers::{
    vec_union_dedup,
    vec_intersect_dedup,
    vec_append_with_dedupe,
    vec_prepend_with_dedupe,
    vec_dedupe_in_place,
    vec_dedupe_in_place_by_key,
    vec_append_dedup_by_key,
    vec_prepend_dedup_by_key,
};
#[cfg(feature = "alloc")]
pub use handlers::{vec_union_dedup_ord, vec_intersect_dedup_ord};

// Export Option helper functions for change detection
pub use handlers::{option_merge_if_changed, option_merge_with_key};