Skip to main content

delta_struct/
unordered.rs

1//! The bridge from a collection to the shape its membership diff takes.
2//!
3//! `#[delta_struct(field_type = "unordered")]` says "diff this by membership
4//! and record nothing about order", and two kinds of collection answer that in
5//! two different shapes. A set's answer is a [`BagDelta`]: these elements came,
6//! these went. A map's answer is an [`EntryDelta`]: a bare key is enough to say
7//! an entry left, because a map cannot hold the same key twice.
8//!
9//! [`Unordered`] is what lets the derive write the right one down without
10//! knowing which it has. An `unordered` field's delta is declared as
11//! `<T as Unordered>::Delta`, and the collection's impl picks the shape.
12//!
13//! Implement it for your own collection to make it eligible, delegating to
14//! [`bag`], to [`entry`], or to something of your
15//! own. A [`Vec`] deliberately has no impl — see the crate's
16//! [Limitations](crate#limitations).
17
18use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
19use std::hash::{BuildHasher, Hash};
20
21use crate::{bag, entry, BagDelta, EntryDelta};
22
23/// A collection that can be diffed by membership alone.
24pub trait Unordered: Sized {
25    /// The shape a membership diff of this collection takes.
26    type Delta: Default;
27
28    /// Computes what it would take to turn `old` into `new`, or [`None`] when
29    /// the two hold the same elements.
30    ///
31    /// The [`None`] mirrors [`Delta::delta`](crate::Delta::delta): it is how a
32    /// field says it has nothing to contribute, so that a struct whose fields
33    /// all say so produces no delta at all.
34    fn diff(old: Self, new: Self) -> Option<Self::Delta>;
35
36    /// Applies a membership diff in place.
37    ///
38    /// Membership is preserved but position is not — additions land wherever
39    /// the collection decides to put them. Unlike
40    /// [`map::apply`](crate::map::apply) this cannot fail, because it never
41    /// recurses into [`Delta::apply_delta`](crate::Delta::apply_delta).
42    fn apply(&mut self, delta: Self::Delta);
43}
44
45impl<T, S> Unordered for HashSet<T, S>
46where
47    T: Hash + Eq,
48    S: BuildHasher,
49{
50    type Delta = BagDelta<T>;
51
52    fn diff(old: Self, new: Self) -> Option<BagDelta<T>> {
53        let delta = bag::diff(old, new);
54        if delta.is_empty() {
55            None
56        } else {
57            Some(delta)
58        }
59    }
60
61    fn apply(&mut self, delta: BagDelta<T>) {
62        bag::apply(self, delta)
63    }
64}
65
66impl<T> Unordered for BTreeSet<T>
67where
68    T: Ord,
69{
70    type Delta = BagDelta<T>;
71
72    fn diff(old: Self, new: Self) -> Option<BagDelta<T>> {
73        let delta = bag::diff(old, new);
74        if delta.is_empty() {
75            None
76        } else {
77            Some(delta)
78        }
79    }
80
81    fn apply(&mut self, delta: BagDelta<T>) {
82        bag::apply(self, delta)
83    }
84}
85
86impl<K, V, S> Unordered for HashMap<K, V, S>
87where
88    K: Hash + Eq,
89    V: PartialEq,
90    S: BuildHasher,
91{
92    type Delta = EntryDelta<K, V>;
93
94    fn diff(old: Self, new: Self) -> Option<EntryDelta<K, V>> {
95        let delta = entry::diff(old, new);
96        if delta.is_empty() {
97            None
98        } else {
99            Some(delta)
100        }
101    }
102
103    fn apply(&mut self, delta: EntryDelta<K, V>) {
104        entry::apply(self, delta)
105    }
106}
107
108impl<K, V> Unordered for BTreeMap<K, V>
109where
110    K: Ord,
111    V: PartialEq,
112{
113    type Delta = EntryDelta<K, V>;
114
115    fn diff(old: Self, new: Self) -> Option<EntryDelta<K, V>> {
116        let delta = entry::diff(old, new);
117        if delta.is_empty() {
118            None
119        } else {
120            Some(delta)
121        }
122    }
123
124    fn apply(&mut self, delta: EntryDelta<K, V>) {
125        entry::apply(self, delta)
126    }
127}