deepmerge/policy.rs
1//! Policy system for configuring merge behavior
2//!
3//! Policies define default behaviors for different types during merging.
4//! They can be overridden at struct, field, or type level.
5
6
7/// Scalar merge action for primitive types and structs.
8///
9/// This enum determines the fundamental merge strategy for scalar values
10/// and can be used to configure whether structs should be recursively merged
11/// or replaced entirely.
12///
13/// # Examples
14///
15/// ```rust
16/// use deepmerge::prelude::*;
17///
18/// // ScalarAction enum demonstrates merge strategies
19/// let replace_action = ScalarAction::Replace;
20/// let keep_action = ScalarAction::Keep;
21/// let merge_action = ScalarAction::Merge;
22///
23/// // Simple demonstration with basic merge
24/// #[derive(DeepMerge, Debug, PartialEq)]
25/// struct Config {
26/// name: String,
27/// value: i32,
28/// }
29///
30/// let mut config1 = Config { name: "app".to_string(), value: 10 };
31/// let config2 = Config { name: "service".to_string(), value: 20 };
32///
33/// // Default merge behavior - recursively merge fields
34/// config1.merge_with_policy(config2, &DefaultPolicy);
35/// assert_eq!(config1.name, "service"); // String uses Replace by default
36/// assert_eq!(config1.value, 20); // i32 uses Replace by default
37/// ```
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39#[non_exhaustive]
40pub enum ScalarAction {
41 /// Replace left with right (default for scalars)
42 Replace,
43 /// Keep left, ignore right
44 Keep,
45 /// Recursively merge (default for structs with `DeepMerge`)
46 Merge,
47}
48
49/// Sequence merge behavior for collections like `Vec<T>`, arrays, and slices.
50///
51/// This enum defines how two sequences should be combined during merging.
52/// Different strategies are useful for different use cases like configuration
53/// merging, data aggregation, or collection operations.
54///
55/// # Examples
56///
57/// ```rust
58/// use deepmerge::prelude::*;
59///
60/// let mut tags1 = vec!["rust", "web"];
61/// let tags2 = vec!["api", "server"];
62///
63/// // Append: add right elements to the end (default)
64/// let policy = ComposedPolicy::new(DefaultPolicy)
65/// .with_sequence_merge(SequenceMerge::Append);
66/// let mut test = tags1.clone();
67/// test.merge_with_policy(tags2.clone(), &policy);
68/// assert_eq!(test, vec!["rust", "web", "api", "server"]);
69///
70/// // Prepend: add right elements to the beginning
71/// let policy = ComposedPolicy::new(DefaultPolicy)
72/// .with_sequence_merge(SequenceMerge::Prepend);
73/// let mut test = tags1.clone();
74/// test.merge_with_policy(tags2.clone(), &policy);
75/// assert_eq!(test, vec!["api", "server", "rust", "web"]);
76///
77/// // Extend: same as Append but optimized for performance
78/// let policy = ComposedPolicy::new(DefaultPolicy)
79/// .with_sequence_merge(SequenceMerge::Extend);
80/// let mut test = tags1.clone();
81/// test.merge_with_policy(tags2.clone(), &policy);
82/// assert_eq!(test, vec!["rust", "web", "api", "server"]);
83/// ```
84///
85/// # Note
86///
87/// `Union` and `Intersect` operations may require additional trait bounds
88/// or special handling depending on the element type.
89#[derive(Debug, Clone, Copy, PartialEq, Eq)]
90#[non_exhaustive]
91pub enum SequenceMerge {
92 /// Append right to left (default)
93 Append,
94 /// Prepend right to left
95 Prepend,
96 /// Extend left with right (like `HashMap::extend`)
97 Extend,
98 /// Union of left and right (dedupe)
99 Union,
100 /// Intersection of left and right
101 Intersect,
102}
103
104/// Option merge behavior for `Option<T>` types.
105///
106/// This enum defines how two `Option` values should be combined,
107/// providing different strategies for handling `Some` and `None` values.
108///
109/// # Examples
110///
111/// ```rust
112/// use deepmerge::prelude::*;
113///
114/// // Take: use right if Some, otherwise keep left (default)
115/// let policy = ComposedPolicy::new(DefaultPolicy)
116/// .with_option_merge(OptionMerge::Take);
117/// let mut left = Some(42);
118/// left.merge_with_policy(Some(100), &policy);
119/// assert_eq!(left, Some(100));
120///
121/// let mut left = Some(42);
122/// left.merge_with_policy(None, &policy);
123/// assert_eq!(left, Some(42));
124///
125/// // Preserve: always keep left, ignore right
126/// let policy = ComposedPolicy::new(DefaultPolicy)
127/// .with_option_merge(OptionMerge::Preserve);
128/// let mut left = Some(42);
129/// left.merge_with_policy(Some(100), &policy);
130/// assert_eq!(left, Some(42));
131///
132/// // OrLeft: use left if Some, otherwise use right
133/// let policy = ComposedPolicy::new(DefaultPolicy)
134/// .with_option_merge(OptionMerge::OrLeft);
135/// let mut left: Option<i32> = None;
136/// left.merge_with_policy(Some(100), &policy);
137/// assert_eq!(left, Some(100));
138/// ```
139#[derive(Debug, Clone, Copy, PartialEq, Eq)]
140#[non_exhaustive]
141pub enum OptionMerge {
142 /// Take right if Some, else keep left (default)
143 Take,
144 /// Always keep left, ignore right
145 Preserve,
146 /// Take left if Some, else take right
147 OrLeft,
148}
149
150/// Number merge behavior for numeric types.
151///
152/// This enum defines different strategies for combining two numeric values,
153/// supporting arithmetic operations and comparisons.
154///
155/// # Examples
156///
157/// ```rust
158/// use deepmerge::prelude::*;
159///
160/// // Sum: add values together
161/// #[derive(DeepMerge)]
162/// #[merge(policy(number = sum))]
163/// struct Config { score: i32 }
164///
165/// let mut config = Config { score: 10 };
166/// config.merge_with_policy(Config { score: 25 }, &DefaultPolicy);
167/// assert_eq!(config.score, 35);
168///
169/// // Max: keep the larger value
170/// #[derive(DeepMerge)]
171/// #[merge(policy(number = max))]
172/// struct MaxConfig { value: i32 }
173///
174/// let mut config = MaxConfig { value: 10 };
175/// config.merge_with_policy(MaxConfig { value: 5 }, &DefaultPolicy);
176/// assert_eq!(config.value, 10);
177/// config.merge_with_policy(MaxConfig { value: 15 }, &DefaultPolicy);
178/// assert_eq!(config.value, 15);
179///
180/// // Min: keep the smaller value
181/// #[derive(DeepMerge)]
182/// #[merge(policy(number = min))]
183/// struct MinConfig { value: i32 }
184///
185/// let mut config = MinConfig { value: 10 };
186/// config.merge_with_policy(MinConfig { value: 5 }, &DefaultPolicy);
187/// assert_eq!(config.value, 5);
188/// config.merge_with_policy(MinConfig { value: 15 }, &DefaultPolicy);
189/// assert_eq!(config.value, 5);
190/// ```
191#[derive(Debug, Clone, Copy, PartialEq, Eq)]
192#[non_exhaustive]
193pub enum NumberMerge {
194 /// Replace with right (default)
195 Replace,
196 /// Keep left, ignore right
197 Keep,
198 /// Take maximum of left and right
199 Max,
200 /// Take minimum of left and right
201 Min,
202 /// Sum left and right
203 Sum,
204}
205
206/// Map merge behavior for `HashMap` and `BTreeMap` types.
207///
208/// This enum defines strategies for combining two maps, handling key conflicts
209/// and nested value merging in different ways.
210///
211/// # Examples
212///
213/// ```rust
214/// use std::collections::HashMap;
215/// use deepmerge::prelude::*;
216///
217/// // Overlay: merge recursively, right wins on conflicts (default)
218/// #[derive(DeepMerge)]
219/// #[merge(policy(map = overlay))]
220/// struct Config {
221/// settings: HashMap<String, i32>,
222/// }
223///
224/// let mut config = Config {
225/// settings: [("a".to_string(), 1), ("b".to_string(), 2)].into(),
226/// };
227/// let update = Config {
228/// settings: [("b".to_string(), 3), ("c".to_string(), 4)].into(),
229/// };
230/// config.merge_with_policy(update, &DefaultPolicy);
231/// // Result: {"a": 1, "b": 3, "c": 4}
232/// assert_eq!(config.settings.get("a"), Some(&1));
233/// assert_eq!(config.settings.get("b"), Some(&3)); // right wins
234/// assert_eq!(config.settings.get("c"), Some(&4));
235///
236/// // Left: keep only left entries
237/// #[derive(DeepMerge)]
238/// #[merge(policy(map = left))]
239/// struct LeftConfig {
240/// data: HashMap<String, i32>,
241/// }
242///
243/// let mut config = LeftConfig {
244/// data: [("a".to_string(), 1), ("b".to_string(), 2)].into(),
245/// };
246/// let update = LeftConfig {
247/// data: [("b".to_string(), 3), ("c".to_string(), 4)].into(),
248/// };
249/// config.merge_with_policy(update, &DefaultPolicy);
250/// // Result: {"a": 1, "b": 2} (unchanged)
251/// assert_eq!(config.data.len(), 2);
252/// assert_eq!(config.data.get("c"), None);
253/// ```
254#[derive(Debug, Clone, Copy, PartialEq, Eq)]
255#[non_exhaustive]
256pub enum MapMerge {
257 /// Overlay right entries, merge values recursively (default)
258 Overlay,
259 /// Union of keys, prefer right on conflicts
260 Union,
261 /// Keep only left entries
262 Left,
263 /// Replace with right entries
264 Right,
265}
266
267/// Boolean merge behavior for `bool` types.
268///
269/// This enum defines logical operations for combining two boolean values,
270/// supporting different prioritization strategies.
271///
272/// # Examples
273///
274/// ```rust
275/// use deepmerge::prelude::*;
276///
277/// // TrueWins: logical OR behavior
278/// #[derive(DeepMerge)]
279/// #[merge(policy(bool = true_wins))]
280/// struct Config { enabled: bool }
281///
282/// let mut config = Config { enabled: false };
283/// config.merge_with_policy(Config { enabled: true }, &DefaultPolicy);
284/// assert_eq!(config.enabled, true);
285///
286/// let mut config = Config { enabled: true };
287/// config.merge_with_policy(Config { enabled: false }, &DefaultPolicy);
288/// assert_eq!(config.enabled, true); // true wins
289///
290/// // FalseWins: logical NOR behavior
291/// #[derive(DeepMerge)]
292/// #[merge(policy(bool = false_wins))]
293/// struct FalseConfig { active: bool }
294///
295/// let mut config = FalseConfig { active: true };
296/// config.merge_with_policy(FalseConfig { active: false }, &DefaultPolicy);
297/// assert_eq!(config.active, false); // false wins
298///
299/// // Keep: ignore right value
300/// #[derive(DeepMerge)]
301/// #[merge(policy(bool = keep))]
302/// struct KeepConfig { flag: bool }
303///
304/// let mut config = KeepConfig { flag: true };
305/// config.merge_with_policy(KeepConfig { flag: false }, &DefaultPolicy);
306/// assert_eq!(config.flag, true); // kept original
307/// ```
308#[derive(Debug, Clone, Copy, PartialEq, Eq)]
309#[non_exhaustive]
310pub enum BoolMerge {
311 /// Replace with right (default)
312 Replace,
313 /// Keep left, ignore right
314 Keep,
315 /// Set to true if either is true
316 TrueWins,
317 /// Set to false if either is false
318 FalseWins,
319}
320
321/// String merge behavior for `String` and `&str` types.
322///
323/// This enum defines different strategies for combining two string values,
324/// including concatenation with optional separators.
325///
326/// # Examples
327///
328/// ```rust
329/// use deepmerge::prelude::*;
330///
331/// // Concat: simple concatenation
332/// #[derive(DeepMerge)]
333/// #[merge(policy(string = concat))]
334/// struct Config { text: String }
335///
336/// let mut config = Config { text: "Hello".to_string() };
337/// config.merge_with_policy(Config { text: " World".to_string() }, &DefaultPolicy);
338/// assert_eq!(config.text, "Hello World");
339///
340/// // ConcatWithSep: concatenation with separator
341/// #[derive(DeepMerge)]
342/// #[merge(policy(string = StringMerge::ConcatWithSep(", ")))]
343/// struct ListConfig { items: String }
344///
345/// let mut config = ListConfig { items: "apple".to_string() };
346/// config.merge_with_policy(ListConfig { items: "banana".to_string() }, &DefaultPolicy);
347/// assert_eq!(config.items, "apple, banana");
348///
349/// // Keep: ignore right value
350/// #[derive(DeepMerge)]
351/// #[merge(policy(string = keep))]
352/// struct KeepConfig { name: String }
353///
354/// let mut config = KeepConfig { name: "original".to_string() };
355/// config.merge_with_policy(KeepConfig { name: "new".to_string() }, &DefaultPolicy);
356/// assert_eq!(config.name, "original");
357///
358/// // Field-level override with path separator
359/// #[derive(DeepMerge)]
360/// struct PathConfig {
361/// #[merge(string = StringMerge::ConcatWithSep(" -> "))]
362/// path: String,
363/// }
364///
365/// let mut config = PathConfig { path: "home".to_string() };
366/// config.merge_with_policy(PathConfig { path: "user".to_string() }, &DefaultPolicy);
367/// assert_eq!(config.path, "home -> user");
368/// ```
369#[derive(Debug, Clone, PartialEq, Eq)]
370#[non_exhaustive]
371pub enum StringMerge {
372 /// Replace left with right (default)
373 Replace,
374 /// Keep left, ignore right
375 Keep,
376 /// Concatenate right to left
377 Concat,
378 /// Concatenate with a separator
379 ConcatWithSep(&'static str),
380}
381
382/// When to apply merge operations (guards/conditions).
383///
384/// This enum provides conditional logic for determining whether a merge
385/// operation should be performed, enabling fine-grained control over when
386/// values are actually merged.
387///
388/// # Examples
389///
390/// ```rust
391/// use deepmerge::prelude::*;
392///
393/// // NonEmpty: only merge when right value is non-empty
394/// #[derive(DeepMerge)]
395/// #[merge(policy(condition = non_empty))]
396/// struct Config { text: String }
397///
398/// let mut config = Config { text: "original".to_string() };
399/// config.merge_with_policy(Config { text: "".to_string() }, &DefaultPolicy);
400/// assert_eq!(config.text, "original"); // empty string ignored
401///
402/// config.merge_with_policy(Config { text: "new".to_string() }, &DefaultPolicy);
403/// assert_eq!(config.text, "new"); // non-empty string merged
404///
405/// // NonDefault: only merge when right is not default value
406/// #[derive(DeepMerge)]
407/// #[merge(policy(condition = non_default))]
408/// struct DefaultConfig { value: i32 }
409///
410/// let mut config = DefaultConfig { value: 42 };
411/// config.merge_with_policy(DefaultConfig { value: 0 }, &DefaultPolicy); // 0 is default for i32
412/// assert_eq!(config.value, 42); // default ignored
413///
414/// config.merge_with_policy(DefaultConfig { value: 99 }, &DefaultPolicy);
415/// assert_eq!(config.value, 99); // non-default merged
416///
417/// // Some: only merge when Option is Some
418/// #[derive(DeepMerge)]
419/// #[merge(policy(condition = some))]
420/// struct OptConfig { data: Option<i32> }
421///
422/// let mut config = OptConfig { data: Some(42) };
423/// config.merge_with_policy(OptConfig { data: None }, &DefaultPolicy);
424/// assert_eq!(config.data, Some(42)); // None ignored
425///
426/// config.merge_with_policy(OptConfig { data: Some(99) }, &DefaultPolicy);
427/// assert_eq!(config.data, Some(99)); // Some merged
428///
429/// // Changed: only merge when values differ
430/// #[derive(DeepMerge)]
431/// #[merge(policy(condition = changed))]
432/// struct ChangeConfig { count: i32 }
433///
434/// let mut config = ChangeConfig { count: 42 };
435/// config.merge_with_policy(ChangeConfig { count: 42 }, &DefaultPolicy);
436/// assert_eq!(config.count, 42); // same value ignored
437///
438/// config.merge_with_policy(ChangeConfig { count: 99 }, &DefaultPolicy);
439/// assert_eq!(config.count, 99); // different value merged
440/// ```
441#[derive(Debug)]
442pub enum Condition {
443 /// Always apply (default)
444 Always,
445 /// Only when right is non-empty (for collections/strings)
446 NonEmpty,
447 /// Only when right is not the default value
448 NonDefault,
449 /// Only when right is Some (for Options)
450 Some,
451 /// Only when right differs from left
452 Changed,
453 /// Only when right differs from left according to a custom key extractor function
454 /// This stores a function that can extract comparable keys from values
455 /// The function signature should be fn(&T) -> K where K: `PartialEq`
456 ChangedBy(fn()),
457}
458
459impl Clone for Condition {
460 fn clone(&self) -> Self {
461 match self {
462 Condition::Always => Condition::Always,
463 Condition::NonEmpty => Condition::NonEmpty,
464 Condition::NonDefault => Condition::NonDefault,
465 Condition::Some => Condition::Some,
466 Condition::Changed => Condition::Changed,
467 Condition::ChangedBy(f) => Condition::ChangedBy(*f),
468 }
469 }
470}
471
472impl PartialEq for Condition {
473 fn eq(&self, other: &Self) -> bool {
474 match (self, other) {
475 (Condition::Always, Condition::Always)
476 | (Condition::NonEmpty, Condition::NonEmpty)
477 | (Condition::NonDefault, Condition::NonDefault)
478 | (Condition::Some, Condition::Some)
479 | (Condition::Changed, Condition::Changed) => true,
480 (Condition::ChangedBy(f1), Condition::ChangedBy(f2)) => core::ptr::eq(std::ptr::from_ref::<fn()>(f1), std::ptr::from_ref::<fn()>(f2)),
481 _ => false,
482 }
483 }
484}
485
486impl Eq for Condition {}
487
488/// Policy trait defining default merge behaviors
489pub trait Policy: Clone {
490 /// Default action for scalars
491 fn scalar_action(&self) -> ScalarAction {
492 ScalarAction::Replace
493 }
494
495 /// Default action for structs that implement `DeepMerge`
496 fn struct_action(&self) -> ScalarAction {
497 ScalarAction::Merge
498 }
499
500 /// Default strategy for Options
501 fn option_merge(&self) -> OptionMerge {
502 OptionMerge::Take
503 }
504
505 /// Default strategy for sequences (Vec, arrays, etc.)
506 fn sequence_merge(&self) -> SequenceMerge {
507 SequenceMerge::Append
508 }
509
510 /// Whether to deduplicate sequences by default
511 fn sequence_dedupe(&self) -> bool {
512 false
513 }
514
515 /// Default strategy for maps (`HashMap`, `BTreeMap`, etc.)
516 fn map_merge(&self) -> MapMerge {
517 MapMerge::Overlay
518 }
519
520 /// Default strategy for numbers
521 fn number_merge(&self) -> NumberMerge {
522 NumberMerge::Replace
523 }
524
525 /// Default strategy for booleans
526 fn bool_merge(&self) -> BoolMerge {
527 BoolMerge::Replace
528 }
529
530 /// Default strategy for strings
531 fn string_merge(&self) -> StringMerge {
532 StringMerge::Replace
533 }
534
535 /// Default when condition
536 fn when_condition(&self) -> Condition {
537 Condition::Always
538 }
539}
540
541/// Default policy with sensible defaults
542#[derive(Debug, Clone, Copy, Default)]
543pub struct DefaultPolicy;
544
545impl Policy for DefaultPolicy {
546 // Uses all the default implementations
547}
548
549/// Policy that always replaces (no merging)
550#[derive(Debug, Clone, Copy, Default)]
551pub struct StrictReplacePolicy;
552
553impl Policy for StrictReplacePolicy {
554 fn struct_action(&self) -> ScalarAction {
555 ScalarAction::Replace
556 }
557
558 fn map_merge(&self) -> MapMerge {
559 MapMerge::Right
560 }
561}
562
563/// Policy that preserves left values
564#[derive(Debug, Clone, Copy, Default)]
565pub struct PreservePolicy;
566
567impl Policy for PreservePolicy {
568 fn scalar_action(&self) -> ScalarAction {
569 ScalarAction::Keep
570 }
571
572 fn struct_action(&self) -> ScalarAction {
573 ScalarAction::Keep
574 }
575
576 fn option_merge(&self) -> OptionMerge {
577 OptionMerge::Preserve
578 }
579
580 fn sequence_merge(&self) -> SequenceMerge {
581 SequenceMerge::Extend // Keep left, ignore right
582 }
583
584 fn map_merge(&self) -> MapMerge {
585 MapMerge::Left
586 }
587
588 fn number_merge(&self) -> NumberMerge {
589 NumberMerge::Keep // Preserve left values
590 }
591
592 fn bool_merge(&self) -> BoolMerge {
593 BoolMerge::Keep
594 }
595
596 fn string_merge(&self) -> StringMerge {
597 StringMerge::Keep
598 }
599
600 fn when_condition(&self) -> Condition {
601 Condition::Always // But we'll ignore the merge anyway due to other settings
602 }
603}
604
605/// Policy that deduplicates collections
606#[derive(Debug, Clone, Copy, Default)]
607pub struct DedupeCollectionsPolicy;
608
609impl Policy for DedupeCollectionsPolicy {
610 fn sequence_merge(&self) -> SequenceMerge {
611 SequenceMerge::Union
612 }
613
614 fn sequence_dedupe(&self) -> bool {
615 true
616 }
617}
618
619/// Composed policy that overrides specific behaviors from a base policy
620/// Used by the derive macro to implement field-level policy overrides
621#[derive(Debug, Clone)]
622pub struct ComposedPolicy<P: Policy> {
623 /// The base policy to use when no override is specified
624 pub base: P,
625 /// Override for scalar merge behavior
626 pub scalar_action: Option<ScalarAction>,
627 /// Override for struct merge behavior
628 pub struct_action: Option<ScalarAction>,
629 /// Override for Option<T> merge behavior
630 pub option_merge: Option<OptionMerge>,
631 /// Override for sequence (Vec, arrays) merge behavior
632 pub sequence_merge: Option<SequenceMerge>,
633 /// Whether to deduplicate sequences after merging
634 pub sequence_dedupe: Option<bool>,
635 /// Override for HashMap/BTreeMap merge behavior
636 pub map_merge: Option<MapMerge>,
637 /// Override for numeric type merge behavior
638 pub number_merge: Option<NumberMerge>,
639 /// Override for boolean merge behavior
640 pub bool_merge: Option<BoolMerge>,
641 /// Override for string merge behavior
642 pub string_merge: Option<StringMerge>,
643 /// Condition for when to apply the merge
644 pub when_condition: Option<Condition>,
645}
646
647impl<P: Policy> ComposedPolicy<P> {
648 /// Create a new composed policy with the given base policy and no overrides
649 pub fn new(base: P) -> Self {
650 Self {
651 base,
652 scalar_action: None,
653 struct_action: None,
654 option_merge: None,
655 sequence_merge: None,
656 sequence_dedupe: None,
657 map_merge: None,
658 number_merge: None,
659 bool_merge: None,
660 string_merge: None,
661 when_condition: None,
662 }
663 }
664
665 /// Set the scalar action for this policy
666 #[must_use]
667 pub fn with_scalar_action(mut self, action: ScalarAction) -> Self {
668 self.scalar_action = Some(action);
669 self
670 }
671
672 /// Set the struct action for this policy
673 #[must_use]
674 pub fn with_struct_action(mut self, action: ScalarAction) -> Self {
675 self.struct_action = Some(action);
676 self
677 }
678
679 /// Set the option merge strategy
680 #[must_use]
681 pub fn with_option_merge(mut self, merge: OptionMerge) -> Self {
682 self.option_merge = Some(merge);
683 self
684 }
685
686 /// Set the sequence merge strategy
687 #[must_use]
688 pub fn with_sequence_merge(mut self, merge: SequenceMerge) -> Self {
689 self.sequence_merge = Some(merge);
690 self
691 }
692
693 /// Set whether to deduplicate sequences
694 #[must_use]
695 pub fn with_sequence_dedupe(mut self, dedupe: bool) -> Self {
696 self.sequence_dedupe = Some(dedupe);
697 self
698 }
699
700 /// Set the map merge strategy
701 #[must_use]
702 pub fn with_map_merge(mut self, merge: MapMerge) -> Self {
703 self.map_merge = Some(merge);
704 self
705 }
706
707 /// Set the number merge strategy
708 #[must_use]
709 pub fn with_number_merge(mut self, merge: NumberMerge) -> Self {
710 self.number_merge = Some(merge);
711 self
712 }
713
714 /// Set the boolean merge strategy
715 #[must_use]
716 pub fn with_bool_merge(mut self, merge: BoolMerge) -> Self {
717 self.bool_merge = Some(merge);
718 self
719 }
720
721 /// Set the string merge strategy
722 #[must_use]
723 pub fn with_string_merge(mut self, merge: StringMerge) -> Self {
724 self.string_merge = Some(merge);
725 self
726 }
727
728 /// Set the when condition
729 #[must_use]
730 pub fn with_when_condition(mut self, condition: Condition) -> Self {
731 self.when_condition = Some(condition);
732 self
733 }
734}
735
736impl<P: Policy> Policy for ComposedPolicy<P> {
737 fn scalar_action(&self) -> ScalarAction {
738 self.scalar_action.unwrap_or_else(|| self.base.scalar_action())
739 }
740
741 fn struct_action(&self) -> ScalarAction {
742 self.struct_action.unwrap_or_else(|| self.base.struct_action())
743 }
744
745 fn option_merge(&self) -> OptionMerge {
746 self.option_merge.unwrap_or_else(|| self.base.option_merge())
747 }
748
749 fn sequence_merge(&self) -> SequenceMerge {
750 self.sequence_merge.unwrap_or_else(|| self.base.sequence_merge())
751 }
752
753 fn sequence_dedupe(&self) -> bool {
754 self.sequence_dedupe.unwrap_or_else(|| self.base.sequence_dedupe())
755 }
756
757 fn map_merge(&self) -> MapMerge {
758 self.map_merge.unwrap_or_else(|| self.base.map_merge())
759 }
760
761 fn number_merge(&self) -> NumberMerge {
762 self.number_merge.unwrap_or_else(|| self.base.number_merge())
763 }
764
765 fn bool_merge(&self) -> BoolMerge {
766 self.bool_merge.unwrap_or_else(|| self.base.bool_merge())
767 }
768
769 fn string_merge(&self) -> StringMerge {
770 self.string_merge.clone().unwrap_or_else(|| self.base.string_merge())
771 }
772
773 fn when_condition(&self) -> Condition {
774 self.when_condition.clone().unwrap_or_else(|| self.base.when_condition())
775 }
776}
777
778/// Result of a merge operation indicating what changed
779#[derive(Debug, Clone, Copy, PartialEq, Eq)]
780pub enum MergeOutcome {
781 /// Nothing was changed during the merge
782 Unchanged,
783 /// Something was changed during the merge
784 Changed,
785}
786
787impl MergeOutcome {
788 /// Check if the merge changed anything
789 #[must_use]
790 pub fn is_changed(&self) -> bool {
791 matches!(self, MergeOutcome::Changed)
792 }
793
794 /// Check if the merge left everything unchanged
795 #[must_use]
796 pub fn is_unchanged(&self) -> bool {
797 matches!(self, MergeOutcome::Unchanged)
798 }
799}
800
801/// Merge trait with policy parameter
802pub trait DeepMerge<P: Policy = DefaultPolicy>: Sized {
803 /// Merge another instance into this one using the specified policy
804 fn merge_with_policy(&mut self, other: Self, policy: &P);
805
806 /// Merge another instance by reference using the specified policy
807 /// This avoids moving the source when possible
808 fn merge_ref(&mut self, src: &Self, policy: &P)
809 where
810 Self: Clone,
811 {
812 self.merge_with_policy(src.clone(), policy);
813 }
814
815 /// Merge and report whether anything changed
816 fn merge_with_policy_reporting(&mut self, other: Self, policy: &P) -> MergeOutcome {
817 // Default implementation just merges and reports changed
818 // Individual types can override for more precise change detection
819 self.merge_with_policy(other, policy);
820 MergeOutcome::Changed
821 }
822
823 /// Merge by reference and report whether anything changed
824 fn merge_ref_reporting(&mut self, src: &Self, policy: &P) -> MergeOutcome
825 where
826 Self: Clone,
827 {
828 self.merge_with_policy_reporting(src.clone(), policy)
829 }
830
831 /// Non-mutating merge using the specified policy
832 #[must_use]
833 fn merged_with_policy(mut self, other: Self, policy: &P) -> Self {
834 self.merge_with_policy(other, policy);
835 self
836 }
837}
838
839/// Merge trait for merging from other types, including references
840/// This allows merging from U into Self without requiring Clone on U
841pub trait DeepMergeFrom<U, P: Policy = DefaultPolicy> {
842 /// Merge from another type/reference using the specified policy
843 fn merge_from_with_policy(&mut self, other: U, policy: &P);
844
845 /// Merge from another type/reference and report whether anything changed
846 fn merge_from_with_policy_reporting(&mut self, other: U, policy: &P) -> MergeOutcome {
847 // Default implementation just merges and reports changed
848 // Individual types can override for more precise change detection
849 self.merge_from_with_policy(other, policy);
850 MergeOutcome::Changed
851 }
852}
853
854/// Extension trait for convenience methods when using `DefaultPolicy`
855///
856/// This trait provides shorthand methods for common merge operations
857/// without requiring explicit policy arguments.
858pub trait DeepMergeDefault: DeepMerge<DefaultPolicy> {
859 /// Merge using the default policy
860 fn merge(&mut self, other: Self) {
861 self.merge_with_policy(other, &DefaultPolicy);
862 }
863
864 /// Merge by reference using the default policy
865 fn merge_ref(&mut self, src: &Self)
866 where
867 Self: Clone,
868 {
869 DeepMerge::merge_ref(self, src, &DefaultPolicy);
870 }
871
872 /// Merge and report changes using the default policy
873 fn merge_reporting(&mut self, other: Self) -> MergeOutcome {
874 self.merge_with_policy_reporting(other, &DefaultPolicy)
875 }
876
877 /// Merge by reference and report changes using the default policy
878 fn merge_ref_reporting(&mut self, src: &Self) -> MergeOutcome
879 where
880 Self: Clone,
881 {
882 DeepMerge::merge_ref_reporting(self, src, &DefaultPolicy)
883 }
884
885 /// Non-mutating merge using the default policy
886 #[must_use]
887 fn merged(self, other: Self) -> Self {
888 self.merged_with_policy(other, &DefaultPolicy)
889 }
890}
891
892// Blanket impl for all types that implement DeepMerge<DefaultPolicy>
893impl<T: DeepMerge<DefaultPolicy>> DeepMergeDefault for T {}
894
895/// Extension trait for `DeepMergeFrom` convenience methods when using `DefaultPolicy`
896pub trait DeepMergeFromDefault<U>: DeepMergeFrom<U, DefaultPolicy> {
897 /// Merge from another type/reference using the default policy
898 fn merge_from(&mut self, other: U) {
899 self.merge_from_with_policy(other, &DefaultPolicy);
900 }
901
902 /// Merge from another type/reference and report changes using the default policy
903 fn merge_from_reporting(&mut self, other: U) -> MergeOutcome {
904 self.merge_from_with_policy_reporting(other, &DefaultPolicy)
905 }
906}
907
908// Blanket impl for all types that implement DeepMergeFrom<U, DefaultPolicy>
909impl<T: DeepMergeFrom<U, DefaultPolicy>, U> DeepMergeFromDefault<U> for T {}
910
911// Bridge implementation: DeepMerge -> DeepMergeFrom for owned types
912impl<T: DeepMerge<P>, P: Policy> DeepMergeFrom<T, P> for T {
913 fn merge_from_with_policy(&mut self, other: T, policy: &P) {
914 self.merge_with_policy(other, policy);
915 }
916
917 fn merge_from_with_policy_reporting(&mut self, other: T, policy: &P) -> MergeOutcome {
918 self.merge_with_policy_reporting(other, policy)
919 }
920}
921
922// Bridge implementation: DeepMerge -> DeepMergeFrom for references (requires Clone)
923impl<T: DeepMerge<P> + Clone, P: Policy> DeepMergeFrom<&T, P> for T {
924 fn merge_from_with_policy(&mut self, other: &T, policy: &P) {
925 self.merge_ref(other, policy);
926 }
927
928 fn merge_from_with_policy_reporting(&mut self, other: &T, policy: &P) -> MergeOutcome {
929 self.merge_ref_reporting(other, policy)
930 }
931}