use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, BTreeSet};
#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum ItemDiff<T> {
Ignore,
Set(T),
Unset,
}
impl<T> Default for ItemDiff<T> {
fn default() -> Self {
ItemDiff::Ignore
}
}
pub trait ItemDiffExt<T> {
fn apply(self, prev: T) -> T;
}
impl<T> ItemDiffExt<Option<T>> for ItemDiff<T> {
fn apply(self, prev: Option<T>) -> Option<T> {
match self {
ItemDiff::Ignore => prev,
ItemDiff::Set(t) => Some(t),
ItemDiff::Unset => None,
}
}
}
impl<T> ItemDiffExt<&mut Option<T>> for ItemDiff<T> {
fn apply(self, prev: &mut Option<T>) -> &mut Option<T> {
match self {
ItemDiff::Ignore => {}
ItemDiff::Set(t) => {
*prev = Some(t);
}
ItemDiff::Unset => {
*prev = None;
}
}
prev
}
}
impl<T: Default> ItemDiffExt<T> for ItemDiff<T> {
fn apply(self, prev: T) -> T {
match self {
ItemDiff::Ignore => prev,
ItemDiff::Set(t) => t,
ItemDiff::Unset => T::default(),
}
}
}
#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum SetDiff<T> {
Add(T),
Remove(T),
Ignore,
}
impl<T> Default for SetDiff<T> {
fn default() -> Self {
SetDiff::Ignore
}
}
pub trait SetDiffExt<T> {
fn apply<I: IntoIterator<Item = SetDiff<T>>>(&mut self, iter: I);
}
impl<T: Ord> SetDiffExt<T> for BTreeSet<T> {
fn apply<I: IntoIterator<Item = SetDiff<T>>>(&mut self, iter: I) {
iter.into_iter().for_each(|item| match item {
SetDiff::Add(t) => {
self.insert(t);
}
SetDiff::Remove(t) => {
self.remove(&t);
}
_ => {}
});
}
}
#[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, Debug)]
#[serde(rename_all = "snake_case")]
pub enum MapDiff<K, V> {
Add { key: K, value: V },
Remove(K),
Ignore,
}
impl<K, V> Default for MapDiff<K, V> {
fn default() -> Self {
MapDiff::Ignore
}
}
pub trait MapDiffExt<K, V> {
fn apply<I: IntoIterator<Item = MapDiff<K, V>>>(&mut self, iter: I);
}
impl<K: Ord, V> MapDiffExt<K, V> for BTreeMap<K, V> {
fn apply<I: IntoIterator<Item = MapDiff<K, V>>>(&mut self, iter: I) {
iter.into_iter().for_each(|item| match item {
MapDiff::Add { key, value } => {
self.insert(key, value);
}
MapDiff::Remove(key) => {
self.remove(&key);
}
_ => {}
});
}
}
#[cfg(test)]
mod test {
use super::*;
use serde_json;
#[test]
fn json_serde() {
let variants = [
(ItemDiff::Ignore, "\"ignore\""),
(ItemDiff::Set(true), "{\"set\":true}"),
(ItemDiff::Unset, "\"unset\""),
];
for (v, s) in variants.iter() {
let string = serde_json::to_string(v).unwrap();
assert_eq!(&string, s);
let value: ItemDiff<bool> = serde_json::from_str(&string).unwrap();
assert_eq!(value, *v);
}
}
#[test]
fn bincode_serde() {
let variants = [
(ItemDiff::Ignore, vec![0, 0, 0, 0]),
(ItemDiff::Set(true), vec![1, 0, 0, 0, 1]),
(ItemDiff::Unset, vec![2, 0, 0, 0]),
];
for (v, s) in variants.iter() {
let data = bincode::serialize(v).unwrap();
assert_eq!(&data, s);
let value: ItemDiff<bool> = bincode::deserialize(&data).unwrap();
assert_eq!(value, *v);
}
}
}