1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
//! Keyed diffing, behind the `unordered-delta` field type.
//!
//! A field marked `#[delta_struct(field_type = "unordered-delta")]` is treated
//! as a bag of key/value entries and represented as a [`MapDelta`]. Entries
//! are paired up by key, and a pair whose value changed is recorded as a
//! [`KeyedDelta`] — the value's own [`Delta`] rather than a removal followed by
//! a re-send of the whole thing. Reach for it when a map's values are
//! themselves large structs that tend to change a field at a time.
//!
//! The derive emits calls to [`diff`] and [`apply`]; you only need this module
//! directly to inspect or construct a delta by hand.
use crate::;
/// An entry that splits into a key and a value.
///
/// This is what lets the derive talk about the `K` and the `V` of a
/// `HashMap<K, V>` when all it can name is the collection's
/// [`Item`](IntoIterator::Item). It is implemented for `(K, V)`, which is what
/// every std map iterates as; implement it yourself only if you have a map
/// whose entry type is not a tuple.
/// A keyed diff between two collections of entries.
///
/// The three parts are deliberately asymmetric, and that asymmetry is the
/// whole point of the field type: [`add`] carries whole entries because the
/// receiver has never seen them, while [`remove`] carries bare keys and
/// [`change`] carries deltas, because for those the receiver already holds the
/// rest.
///
/// Nothing here records position — see [`SeqDelta`](crate::SeqDelta) for that.
///
/// [`add`]: MapDelta::add
/// [`remove`]: MapDelta::remove
/// [`change`]: MapDelta::change
/// A change to the value stored under one key.
///
/// Turn on the `serde` feature to get `Serialize` and `Deserialize` on this,
/// as a delta struct with an `unordered-delta` field cannot derive them
/// otherwise.
/// Pairs the entries of `old` and `new` by key and diffs the values that
/// survived.
///
/// A key present on both sides with an equal value produces nothing at all, so
/// the [`MapDelta`] is empty when the collections agree.
///
/// Each key of `old` is looked up in `new` exactly once, through the
/// collection's own [`TryIndex`] implementation, so the cost is that of n
/// lookups: O(n) for a [`HashMap`](std::collections::HashMap), O(n log n) for
/// a [`BTreeMap`](std::collections::BTreeMap).
///
/// ```
/// use delta_struct::{map, Delta, ScalarDelta};
/// use std::collections::BTreeMap;
///
/// #[derive(Delta)]
/// #[delta_struct(delta_leader = "#[derive(Debug, PartialEq)]")]
/// struct Service {
/// port: u16,
/// healthy: bool,
/// }
///
/// let services = |port| {
/// vec![("web", Service { port, healthy: true })]
/// .into_iter()
/// .collect::<BTreeMap<&str, Service>>()
/// };
///
/// let delta = map::diff(services(80), services(8080));
/// assert!(delta.add.is_empty() && delta.remove.is_empty());
/// assert_eq!(delta.change[0].key, "web");
/// assert_eq!(delta.change[0].delta.port, ScalarDelta::Changed(8080));
/// assert_eq!(delta.change[0].delta.healthy, ScalarDelta::Unchanged);
/// ```
/// Applies a keyed diff to `target` in place.
///
/// Removals and changes are single lookups rather than scans, so this costs
/// the same as [`diff`] does, and a changed value is updated where it sits
/// rather than taken out and put back. As with an `unordered` field,
/// membership is preserved but position is not.
///
/// A key in `remove` or `change` that `target` does not have is ignored.
///
/// This is the one collection helper that can fail, because it is the one that
/// recurses into [`Delta::apply_delta`]: a map of enums can be handed a change
/// whose value has since moved to another variant. The error propagates rather
/// than being swallowed, and leaves the map partly updated.
///
/// ```
/// use delta_struct::{map, Delta};
/// use std::collections::BTreeMap;
///
/// #[derive(Delta)]
/// struct Service {
/// port: u16,
/// }
///
/// let services = |port| {
/// vec![("web", Service { port })]
/// .into_iter()
/// .collect::<BTreeMap<&str, Service>>()
/// };
///
/// let delta = map::diff(services(80), services(8080));
/// let mut target = services(80);
/// map::apply(&mut target, delta)?;
/// assert_eq!(target["web"].port, 8080);
/// # Ok::<(), delta_struct::Mismatch>(())
/// ```