pub trait DeepMerge {
fn merge_from(&mut self, other: Self);
}
macro_rules! default_overwriting_impl {
() => {
fn merge_from(&mut self, other: Self) {
*self = other;
}
};
}
impl DeepMerge for bool { default_overwriting_impl! {} }
impl DeepMerge for i32 { default_overwriting_impl! {} }
impl DeepMerge for i64 { default_overwriting_impl! {} }
impl DeepMerge for f64 { default_overwriting_impl! {} }
impl DeepMerge for std::string::String { default_overwriting_impl! {} }
impl DeepMerge for crate::ByteString { default_overwriting_impl! {} }
impl<Tz> DeepMerge for chrono::DateTime<Tz> where Tz: chrono::TimeZone { default_overwriting_impl! {} }
impl DeepMerge for serde_json::Value {
fn merge_from(&mut self, other: Self) {
if let serde_json::Value::Object(this) = self {
if let serde_json::Value::Object(other) = other {
for (k, v) in other {
if v.is_null() {
this.remove(&k);
}
else {
this.entry(k).or_insert(serde_json::Value::Null).merge_from(v);
}
}
return;
}
}
*self = other;
}
}
impl<T> DeepMerge for std::boxed::Box<T> where T: DeepMerge {
fn merge_from(&mut self, other: Self) {
(**self).merge_from(*other);
}
}
impl<T> DeepMerge for Option<T> where T: DeepMerge {
fn merge_from(&mut self, other: Self) {
if let Some(other) = other {
if let Some(s) = self {
s.merge_from(other);
}
else {
*self = Some(other);
}
}
}
}
pub mod strategies {
pub mod list {
mod private {
pub trait AsOptVec {
type Item;
fn set_if_some(&mut self, new: Self);
fn as_mut_opt(&mut self) -> Option<&mut std::vec::Vec<Self::Item>>;
fn into_opt(self) -> Option<std::vec::Vec<Self::Item>>;
}
impl<T> AsOptVec for std::vec::Vec<T> {
type Item = T;
fn set_if_some(&mut self, new: Self) {
*self = new;
}
fn as_mut_opt(&mut self) -> Option<&mut Self> {
Some(self)
}
fn into_opt(self) -> Option<Self> {
Some(self)
}
}
impl<T> AsOptVec for Option<std::vec::Vec<T>> {
type Item = T;
fn set_if_some(&mut self, new: Self) {
if new.is_some() {
*self = new;
}
}
fn as_mut_opt(&mut self) -> Option<&mut std::vec::Vec<T>> {
self.as_mut()
}
fn into_opt(self) -> Self {
self
}
}
}
use private::AsOptVec;
pub fn atomic<V>(current: &mut V, new: V) where V: AsOptVec {
current.set_if_some(new);
}
pub fn map<V>(
current: &mut V,
new: V,
key_comparators: &[fn(&V::Item, &V::Item) -> bool],
merge_item: fn(&mut V::Item, V::Item),
)
where
V: AsOptVec,
{
if let Some(current) = current.as_mut_opt() {
for new_item in new.into_opt().into_iter().flatten() {
if let Some(current_item) = current.iter_mut().find(|current_item| key_comparators.iter().all(|&f| f(&**current_item, &new_item))) {
merge_item(current_item, new_item);
}
else {
current.push(new_item);
}
}
}
else {
current.set_if_some(new);
}
}
pub fn set<V>(current: &mut V, new: V) where V: AsOptVec, V::Item: PartialEq {
if let Some(current) = current.as_mut_opt() {
for item in new.into_opt().into_iter().flatten() {
if !current.contains(&item) {
current.push(item);
}
}
}
else {
current.set_if_some(new);
}
}
}
pub mod map {
mod private {
use std::collections::BTreeMap;
pub trait AsOptMap {
type Key;
type Value;
fn set_if_some(&mut self, new: Self);
fn as_mut_opt(&mut self) -> Option<&mut BTreeMap<Self::Key, Self::Value>>;
fn into_opt(self) -> Option<BTreeMap<Self::Key, Self::Value>>;
}
impl<K, V> AsOptMap for BTreeMap<K, V> {
type Key = K;
type Value = V;
fn set_if_some(&mut self, new: Self) {
*self = new;
}
fn as_mut_opt(&mut self) -> Option<&mut Self> {
Some(self)
}
fn into_opt(self) -> Option<Self> {
Some(self)
}
}
impl<K, V> AsOptMap for Option<BTreeMap<K, V>> {
type Key = K;
type Value = V;
fn set_if_some(&mut self, new: Self) {
if new.is_some() {
*self = new;
}
}
fn as_mut_opt(&mut self) -> Option<&mut BTreeMap<K, V>> {
self.as_mut()
}
fn into_opt(self) -> Self {
self
}
}
}
use private::AsOptMap;
pub fn atomic<M>(current: &mut M, new: M) where M: AsOptMap {
current.set_if_some(new);
}
pub fn granular<M>(current: &mut M, new: M, merge_value: fn(&mut M::Value, M::Value))
where
M: AsOptMap,
M::Key: Ord,
{
if let Some(current) = current.as_mut_opt() {
for (k, new_v) in new.into_opt().into_iter().flatten() {
match current.entry(k) {
std::collections::btree_map::Entry::Vacant(entry) => { entry.insert(new_v); }
std::collections::btree_map::Entry::Occupied(entry) => merge_value(entry.into_mut(), new_v),
}
}
}
else {
current.set_if_some(new);
}
}
}
}