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}