use crate::policy::{Policy, Condition, NumberMerge, BoolMerge, DeepMergeFrom, OptionMerge, SequenceMerge, MapMerge};
use crate::DeepMerge;
#[allow(clippy::inline_always)]
#[inline(always)]
fn should_merge_string<P: Policy>(dst: &str, src: &str, policy: &P) -> bool {
match policy.when_condition() {
Condition::NonEmpty | Condition::NonDefault => !src.is_empty(),
Condition::Always | Condition::Some => true, Condition::Changed | Condition::ChangedBy(_) => dst != src,
}
}
#[allow(clippy::inline_always)]
#[inline(always)]
fn should_merge_vec<T, P: Policy>(src: &[T], policy: &P) -> bool {
match policy.when_condition() {
Condition::NonEmpty | Condition::NonDefault => !src.is_empty(),
Condition::Always | Condition::Some | Condition::Changed | Condition::ChangedBy(_) => true, }
}
#[allow(clippy::inline_always)]
#[inline(always)]
fn should_merge_number<T: PartialEq + Default, P: Policy>(dst: &T, src: &T, policy: &P) -> bool {
match policy.when_condition() {
Condition::Always | Condition::NonEmpty | Condition::Some => true, Condition::NonDefault => *src != T::default(),
Condition::Changed | Condition::ChangedBy(_) => dst != src,
}
}
#[allow(clippy::inline_always)]
#[inline(always)]
fn should_merge_bool<P: Policy>(dst: bool, src: bool, policy: &P) -> bool {
match policy.when_condition() {
Condition::Always | Condition::NonEmpty | Condition::Some => true, Condition::NonDefault => src, Condition::Changed | Condition::ChangedBy(_) => dst != src,
}
}
#[allow(clippy::inline_always)]
#[inline(always)]
fn should_merge_map<K, V, P: Policy>(other: &impl MapLike<K, V>, policy: &P) -> bool {
match policy.when_condition() {
Condition::NonEmpty | Condition::NonDefault => !other.is_empty(),
Condition::Always | Condition::Some | Condition::Changed | Condition::ChangedBy(_) => true,
}
}
#[allow(clippy::inline_always)]
#[inline(always)]
fn should_merge_set<T, P: Policy>(other: &impl SetLike<T>, policy: &P) -> bool {
match policy.when_condition() {
Condition::NonEmpty | Condition::NonDefault => !other.is_empty(),
Condition::Always | Condition::Some | Condition::Changed | Condition::ChangedBy(_) => true,
}
}
trait MapLike<K, V> {
fn is_empty(&self) -> bool;
}
trait SetLike<T> {
fn is_empty(&self) -> bool;
}
#[cfg(feature = "std")]
impl<K, V> MapLike<K, V> for HashMap<K, V> {
fn is_empty(&self) -> bool { self.is_empty() }
}
#[cfg(feature = "std")]
impl<K, V> MapLike<K, V> for BTreeMap<K, V> {
fn is_empty(&self) -> bool { self.is_empty() }
}
#[cfg(feature = "indexmap")]
impl<K, V> MapLike<K, V> for IndexMap<K, V> {
fn is_empty(&self) -> bool { self.is_empty() }
}
#[cfg(feature = "std")]
impl<T> SetLike<T> for HashSet<T> {
fn is_empty(&self) -> bool { self.is_empty() }
}
#[cfg(feature = "std")]
impl<T> SetLike<T> for BTreeSet<T> {
fn is_empty(&self) -> bool { self.is_empty() }
}
#[cfg(feature = "indexmap")]
impl<T> SetLike<T> for IndexSet<T> {
fn is_empty(&self) -> bool { self.is_empty() }
}
#[cfg(feature = "serde_json")]
mod serde_json;
#[cfg(feature = "toml")]
mod toml_value;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
#[cfg(feature = "alloc")]
use alloc::string::String;
#[cfg(feature = "alloc")]
use alloc::collections::LinkedList;
#[cfg(feature = "alloc")]
use alloc::collections::VecDeque;
#[cfg(feature = "std")]
use std::collections::{HashMap, HashSet, BTreeMap, BTreeSet};
#[cfg(any(feature = "std", feature = "indexmap"))]
use core::hash::Hash;
#[cfg(feature = "indexmap")]
use indexmap::{IndexMap, IndexSet};
macro_rules! impl_number_merge {
($($t:ty),*) => {
$(
impl<P: Policy> DeepMerge<P> for $t {
fn merge_with_policy(&mut self, other: Self, policy: &P) {
if !should_merge_number(self, &other, policy) {
return;
}
match policy.number_merge() {
NumberMerge::Replace => *self = other,
NumberMerge::Keep => {}, NumberMerge::Max => *self = (*self).max(other),
NumberMerge::Min => *self = (*self).min(other),
NumberMerge::Sum => *self += other,
}
}
}
)*
}
}
impl_number_merge!(i8, i16, i32, i64, i128, isize);
impl_number_merge!(u8, u16, u32, u64, u128, usize);
impl_number_merge!(f32, f64);
impl<P: Policy> DeepMerge<P> for bool {
fn merge_with_policy(&mut self, other: Self, policy: &P) {
if !should_merge_bool(*self, other, policy) {
return;
}
match policy.bool_merge() {
BoolMerge::Replace => *self = other,
BoolMerge::Keep => {},
BoolMerge::TrueWins => {
if other {
*self = true;
}
},
BoolMerge::FalseWins => {
if !other {
*self = false;
}
},
}
}
}
#[cfg(feature = "alloc")]
impl<P: Policy> DeepMerge<P> for String {
fn merge_with_policy(&mut self, other: Self, policy: &P) {
if !should_merge_string(self, &other, policy) {
return;
}
match policy.string_merge() {
crate::policy::StringMerge::Replace => *self = other,
crate::policy::StringMerge::Keep => {},
crate::policy::StringMerge::Concat => {
self.push_str(&other);
},
crate::policy::StringMerge::ConcatWithSep(sep) => {
if !self.is_empty() && !other.is_empty() {
self.push_str(sep);
}
self.push_str(&other);
},
}
}
fn merge_ref(&mut self, src: &Self, policy: &P)
where
Self: Clone,
{
if !should_merge_string(self, src, policy) {
return;
}
match policy.string_merge() {
crate::policy::StringMerge::Replace => self.clone_from(src),
crate::policy::StringMerge::Keep => {},
crate::policy::StringMerge::Concat => {
self.push_str(src);
},
crate::policy::StringMerge::ConcatWithSep(sep) => {
if !self.is_empty() && !src.is_empty() {
self.push_str(sep);
}
self.push_str(src);
},
}
}
fn merge_with_policy_reporting(&mut self, other: Self, policy: &P) -> crate::policy::MergeOutcome {
let old_value = self.clone();
self.merge_with_policy(other, policy);
if *self == old_value {
crate::policy::MergeOutcome::Unchanged
} else {
crate::policy::MergeOutcome::Changed
}
}
fn merge_ref_reporting(&mut self, src: &Self, policy: &P) -> crate::policy::MergeOutcome
where
Self: Clone,
{
if !should_merge_string(self, src, policy) {
return crate::policy::MergeOutcome::Unchanged;
}
let will_change = match policy.string_merge() {
crate::policy::StringMerge::Replace => *self != *src,
crate::policy::StringMerge::Keep => false,
crate::policy::StringMerge::Concat => !src.is_empty(),
crate::policy::StringMerge::ConcatWithSep(sep) => {
!src.is_empty() && (self.is_empty() || !sep.is_empty())
},
};
if will_change {
match policy.string_merge() {
crate::policy::StringMerge::Replace => self.clone_from(src),
crate::policy::StringMerge::Keep => {}, crate::policy::StringMerge::Concat => {
self.push_str(src);
},
crate::policy::StringMerge::ConcatWithSep(sep) => {
if !self.is_empty() && !src.is_empty() {
self.push_str(sep);
}
self.push_str(src);
},
}
crate::policy::MergeOutcome::Changed
} else {
crate::policy::MergeOutcome::Unchanged
}
}
}
#[cfg(feature = "alloc")]
impl<P: Policy> DeepMergeFrom<&str, P> for String {
fn merge_from_with_policy(&mut self, other: &str, policy: &P) {
if !should_merge_string(self, other, policy) {
return;
}
match policy.string_merge() {
crate::policy::StringMerge::Replace => {
self.clear();
self.push_str(other);
},
crate::policy::StringMerge::Keep => {},
crate::policy::StringMerge::Concat => {
self.push_str(other);
},
crate::policy::StringMerge::ConcatWithSep(sep) => {
if !self.is_empty() && !other.is_empty() {
self.push_str(sep);
}
self.push_str(other);
},
}
}
fn merge_from_with_policy_reporting(&mut self, other: &str, policy: &P) -> crate::policy::MergeOutcome {
if !should_merge_string(self, other, policy) {
return crate::policy::MergeOutcome::Unchanged;
}
let will_change = match policy.string_merge() {
crate::policy::StringMerge::Replace => *self != other,
crate::policy::StringMerge::Keep => false,
crate::policy::StringMerge::Concat => !other.is_empty(),
crate::policy::StringMerge::ConcatWithSep(sep) => {
!other.is_empty() && (self.is_empty() || !sep.is_empty())
},
};
if will_change {
match policy.string_merge() {
crate::policy::StringMerge::Replace => {
self.clear();
self.push_str(other);
},
crate::policy::StringMerge::Keep => {}, crate::policy::StringMerge::Concat => {
self.push_str(other);
},
crate::policy::StringMerge::ConcatWithSep(sep) => {
if !self.is_empty() && !other.is_empty() {
self.push_str(sep);
}
self.push_str(other);
},
}
crate::policy::MergeOutcome::Changed
} else {
crate::policy::MergeOutcome::Unchanged
}
}
}
impl<T: DeepMerge<P>, P: Policy> DeepMerge<P> for Option<T> {
fn merge_with_policy(&mut self, other: Self, policy: &P) {
if !should_merge_option(self, &other, policy) {
return;
}
match policy.option_merge() {
OptionMerge::Take => {
match (self, other) {
(Some(dst), Some(src)) => dst.merge_with_policy(src, policy),
(dst, Some(src)) => *dst = Some(src),
_ => {},
}
},
OptionMerge::Preserve => {
},
OptionMerge::OrLeft => {
if self.is_none() {
*self = other;
}
},
}
}
}
#[allow(clippy::ref_option)]
fn should_merge_option<T, P: Policy>(_dst: &Option<T>, src: &Option<T>, policy: &P) -> bool {
match policy.when_condition() {
Condition::Some | Condition::NonEmpty | Condition::NonDefault => {
src.is_some()
},
Condition::Always | Condition::Changed | Condition::ChangedBy(_) => true,
}
}
pub fn option_merge_if_changed<T, P>(dst: &mut Option<T>, src: Option<T>, policy: &P)
where
T: DeepMerge<P> + PartialEq,
P: Policy,
{
if dst != &src {
match policy.option_merge() {
OptionMerge::Take => {
match (dst, src) {
(Some(d), Some(s)) => d.merge_with_policy(s, policy),
(d, Some(s)) => *d = Some(s),
(d, None) => *d = None, }
},
OptionMerge::Preserve => {
},
OptionMerge::OrLeft => {
if dst.is_none() {
*dst = src;
}
},
}
}
}
pub fn option_merge_with_key<T, P, K, F>(dst: &mut Option<T>, src: Option<T>, policy: &P, key_fn: F)
where
T: DeepMerge<P>,
P: Policy,
K: PartialEq,
F: for<'a> Fn(&'a T) -> K,
{
let should_merge = match (dst.as_ref(), src.as_ref()) {
(Some(d), Some(s)) => key_fn(d) != key_fn(s),
(None, Some(_)) | (Some(_), None) => true,
(None, None) => false,
};
if should_merge {
match policy.option_merge() {
OptionMerge::Take => {
match (dst, src) {
(Some(d), Some(s)) => d.merge_with_policy(s, policy),
(d, Some(s)) => *d = Some(s),
(d, None) => *d = None, }
},
OptionMerge::Preserve => {
},
OptionMerge::OrLeft => {
if dst.is_none() {
*dst = src;
}
},
}
}
}
#[cfg(feature = "alloc")]
impl<T, P: Policy> DeepMerge<P> for Vec<T> {
fn merge_with_policy(&mut self, other: Self, policy: &P) {
if !should_merge_vec(&other, policy) {
return;
}
match policy.sequence_merge() {
SequenceMerge::Append | SequenceMerge::Extend => {
self.reserve(other.len());
self.extend(other);
},
SequenceMerge::Prepend => {
let mut result = other;
result.reserve(self.len());
result.extend(core::mem::take(self));
*self = result;
},
SequenceMerge::Union | SequenceMerge::Intersect => {
self.reserve(other.len());
self.extend(other);
},
}
}
}
#[cfg(feature = "alloc")]
impl<T, P: Policy> DeepMerge<P> for VecDeque<T> {
fn merge_with_policy(&mut self, other: Self, policy: &P) {
let proceed = match policy.when_condition() {
Condition::NonEmpty | Condition::NonDefault => !other.is_empty(),
Condition::Always | Condition::Some | Condition::Changed | Condition::ChangedBy(_) => true,
};
if !proceed { return; }
match policy.sequence_merge() {
SequenceMerge::Append | SequenceMerge::Extend | SequenceMerge::Union | SequenceMerge::Intersect => {
let mut other = other;
self.append(&mut other);
},
SequenceMerge::Prepend => {
let mut result = other;
result.append(self);
*self = result;
},
}
}
}
#[cfg(feature = "alloc")]
impl<T, P: Policy> DeepMerge<P> for LinkedList<T> {
fn merge_with_policy(&mut self, mut other: Self, policy: &P) {
let proceed = match policy.when_condition() {
Condition::NonEmpty | Condition::NonDefault => !other.is_empty(),
Condition::Always | Condition::Some | Condition::Changed | Condition::ChangedBy(_) => true,
};
if !proceed { return; }
match policy.sequence_merge() {
SequenceMerge::Append | SequenceMerge::Extend | SequenceMerge::Union | SequenceMerge::Intersect => {
self.append(&mut other);
},
SequenceMerge::Prepend => {
other.append(self);
*self = other;
},
}
}
}
#[cfg(all(feature = "std", feature = "alloc"))]
pub fn vec_append_with_dedupe<T: Clone + Eq + Hash>(vec: &mut Vec<T>, other: Vec<T>) {
use std::collections::HashSet;
let mut seen: HashSet<T> = HashSet::with_capacity(vec.len() + other.len());
let mut result = Vec::with_capacity(vec.len() + other.len());
for item in core::mem::take(vec) {
if seen.insert(item.clone()) {
result.push(item);
}
}
for item in other {
if seen.insert(item.clone()) {
result.push(item);
}
}
*vec = result;
}
#[cfg(all(feature = "std", feature = "alloc"))]
pub fn vec_prepend_with_dedupe<T: Clone + Eq + Hash>(vec: &mut Vec<T>, other: Vec<T>) {
use std::collections::HashSet;
let mut seen: HashSet<T> = HashSet::with_capacity(vec.len() + other.len());
let mut result = Vec::with_capacity(vec.len() + other.len());
for item in other {
if seen.insert(item.clone()) {
result.push(item);
}
}
for item in core::mem::take(vec) {
if seen.insert(item.clone()) {
result.push(item);
}
}
*vec = result;
}
#[cfg(all(feature = "std", feature = "alloc"))]
pub fn vec_union_dedup<T: Clone + Eq + Hash>(vec: &mut Vec<T>, other: Vec<T>) {
use std::collections::HashSet;
let mut seen: HashSet<T> = HashSet::with_capacity(vec.len() + other.len());
let mut result = Vec::with_capacity(vec.len() + other.len());
for item in core::mem::take(vec) {
if seen.insert(item.clone()) {
result.push(item);
}
}
for item in other {
if seen.insert(item.clone()) {
result.push(item);
}
}
*vec = result;
}
#[cfg(all(feature = "std", feature = "alloc"))]
pub fn vec_intersect_dedup<T: Clone + Eq + Hash>(vec: &mut Vec<T>, other: Vec<T>) {
use std::collections::HashSet;
let other_set: HashSet<T> = other.into_iter().collect();
let mut seen = HashSet::new();
let mut result = Vec::new();
for item in core::mem::take(vec) {
if other_set.contains(&item) && seen.insert(item.clone()) {
result.push(item);
}
}
*vec = result;
}
#[cfg(feature = "alloc")]
pub fn vec_union_dedup_ord<T: Clone + Ord>(vec: &mut Vec<T>, other: Vec<T>) {
use alloc::collections::BTreeSet;
let mut seen = BTreeSet::new();
let mut result = Vec::with_capacity(vec.len() + other.len());
for item in core::mem::take(vec) {
if seen.insert(item.clone()) {
result.push(item);
}
}
for item in other {
if seen.insert(item.clone()) {
result.push(item);
}
}
*vec = result;
}
#[cfg(feature = "alloc")]
pub fn vec_intersect_dedup_ord<T: Clone + Ord>(vec: &mut Vec<T>, other: Vec<T>) {
use alloc::collections::BTreeSet;
let other_set: BTreeSet<T> = other.into_iter().collect();
let mut seen = BTreeSet::new();
let mut result = Vec::new();
for item in core::mem::take(vec) {
if other_set.contains(&item) && seen.insert(item.clone()) {
result.push(item);
}
}
*vec = result;
}
#[cfg(all(feature = "std", feature = "alloc"))]
pub fn vec_dedupe_in_place<T: Clone + Eq + Hash>(vec: &mut Vec<T>) {
use std::collections::HashSet;
let mut seen: HashSet<T> = HashSet::with_capacity(vec.len());
let mut result = Vec::with_capacity(vec.len());
for item in core::mem::take(vec) {
if seen.insert(item.clone()) {
result.push(item);
}
}
*vec = result;
}
#[cfg(all(feature = "std", feature = "alloc"))]
pub fn vec_dedupe_in_place_by_key<T: Clone, K: Eq + Hash, F>(vec: &mut Vec<T>, key_fn: F)
where
F: for<'a> Fn(&'a T) -> K,
{
use std::collections::HashSet;
let mut seen: HashSet<K> = HashSet::with_capacity(vec.len());
let mut result = Vec::with_capacity(vec.len());
for item in core::mem::take(vec) {
let key = key_fn(&item);
if seen.insert(key) {
result.push(item);
}
}
*vec = result;
}
#[cfg(all(feature = "std", feature = "alloc"))]
pub fn vec_append_dedup_by_key<T: Clone, K: Eq + Hash, F>(vec: &mut Vec<T>, other: Vec<T>, key_fn: F)
where
F: for<'a> Fn(&'a T) -> K,
{
use std::collections::HashSet;
let mut seen: HashSet<K> = HashSet::with_capacity(vec.len() + other.len());
let mut result = Vec::with_capacity(vec.len() + other.len());
for item in core::mem::take(vec) {
let key = key_fn(&item);
if seen.insert(key) {
result.push(item);
}
}
for item in other {
let key = key_fn(&item);
if seen.insert(key) {
result.push(item);
}
}
*vec = result;
}
#[cfg(all(feature = "std", feature = "alloc"))]
pub fn vec_prepend_dedup_by_key<T: Clone, K: Eq + Hash, F>(vec: &mut Vec<T>, other: Vec<T>, key_fn: F)
where
F: for<'a> Fn(&'a T) -> K,
{
use std::collections::HashSet;
let mut seen: HashSet<K> = HashSet::with_capacity(vec.len() + other.len());
let mut result = Vec::with_capacity(vec.len() + other.len());
for item in other {
let key = key_fn(&item);
if seen.insert(key) {
result.push(item);
}
}
for item in core::mem::take(vec) {
let key = key_fn(&item);
if seen.insert(key) {
result.push(item);
}
}
*vec = result;
}
#[cfg(feature = "std")]
#[allow(clippy::implicit_hasher)]
impl<K: Eq + Hash, V: DeepMerge<P>, P: Policy> DeepMerge<P> for HashMap<K, V> {
fn merge_with_policy(&mut self, other: Self, policy: &P) {
use std::collections::hash_map::Entry;
if !should_merge_map(&other, policy) {
return;
}
match policy.map_merge() {
MapMerge::Overlay => {
for (k, v) in other {
match self.entry(k) {
Entry::Occupied(mut e) => {
e.get_mut().merge_with_policy(v, policy);
},
Entry::Vacant(e) => {
e.insert(v);
},
}
}
},
MapMerge::Union => {
self.extend(other);
},
MapMerge::Left => {
},
MapMerge::Right => {
*self = other;
},
}
}
}
#[cfg(feature = "std")]
impl<K: Ord, V: DeepMerge<P>, P: Policy> DeepMerge<P> for BTreeMap<K, V> {
fn merge_with_policy(&mut self, other: Self, policy: &P) {
use std::collections::btree_map::Entry;
if !should_merge_map(&other, policy) {
return;
}
match policy.map_merge() {
MapMerge::Overlay => {
for (k, v) in other {
match self.entry(k) {
Entry::Occupied(mut e) => {
e.get_mut().merge_with_policy(v, policy);
},
Entry::Vacant(e) => {
e.insert(v);
},
}
}
},
MapMerge::Union => {
self.extend(other);
},
MapMerge::Left => {
},
MapMerge::Right => {
*self = other;
},
}
}
}
#[cfg(feature = "std")]
#[allow(clippy::implicit_hasher)]
impl<T: Eq + Hash, P: Policy> DeepMerge<P> for HashSet<T> {
fn merge_with_policy(&mut self, other: Self, policy: &P) {
use core::mem;
if !should_merge_set(&other, policy) {
return;
}
match policy.sequence_merge() {
SequenceMerge::Append | SequenceMerge::Extend | SequenceMerge::Union => {
self.extend(other);
},
SequenceMerge::Prepend => {
let mut result = other;
result.extend(mem::take(self));
*self = result;
},
SequenceMerge::Intersect => {
let other_ref = &other;
self.retain(|item| other_ref.contains(item));
},
}
}
}
#[cfg(feature = "std")]
impl<T: Ord, P: Policy> DeepMerge<P> for BTreeSet<T> {
fn merge_with_policy(&mut self, other: Self, policy: &P) {
use core::mem;
if !should_merge_set(&other, policy) {
return;
}
match policy.sequence_merge() {
SequenceMerge::Append | SequenceMerge::Extend | SequenceMerge::Union => {
self.extend(other);
},
SequenceMerge::Prepend => {
let mut result = other;
result.extend(mem::take(self));
*self = result;
},
SequenceMerge::Intersect => {
let other_ref = &other;
self.retain(|item| other_ref.contains(item));
},
}
}
}
#[cfg(feature = "indexmap")]
impl<K: Eq + Hash, V: DeepMerge<P>, P: Policy> DeepMerge<P> for IndexMap<K, V> {
fn merge_with_policy(&mut self, other: Self, policy: &P) {
use indexmap::map::Entry;
if !should_merge_map(&other, policy) {
return;
}
match policy.map_merge() {
MapMerge::Overlay => {
for (k, v) in other {
match self.entry(k) {
Entry::Occupied(mut e) => {
e.get_mut().merge_with_policy(v, policy);
},
Entry::Vacant(e) => {
e.insert(v);
},
}
}
},
MapMerge::Union => {
self.extend(other);
},
MapMerge::Left => {
},
MapMerge::Right => {
*self = other;
},
}
}
}
#[cfg(feature = "indexmap")]
impl<T: Eq + Hash, P: Policy> DeepMerge<P> for IndexSet<T> {
fn merge_with_policy(&mut self, other: Self, policy: &P) {
use core::mem;
if !should_merge_set(&other, policy) {
return;
}
match policy.sequence_merge() {
SequenceMerge::Append | SequenceMerge::Extend | SequenceMerge::Union => {
self.extend(other);
},
SequenceMerge::Prepend => {
let mut result = other;
result.extend(mem::take(self));
*self = result;
},
SequenceMerge::Intersect => {
let other_ref = &other;
self.retain(|item| other_ref.contains(item));
},
}
}
}