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
//! Keyed membership diffing, behind the `unordered` field type when the field
//! is a map.
//!
//! A map is a bag of entries, but a bag with a rule that the plain
//! [`bag`](crate::bag) knows nothing about: no two entries share a key. That
//! rule is what makes an [`EntryDelta`] the right shape for one — a bare key
//! is enough to say an entry left, and a value is enough to say what a key
//! holds now, because putting one back cannot leave the old one standing
//! beside it.
//!
//! Values are compared with `==` and replaced wholesale, which is the whole of
//! what separates this from [`map`](crate::map): reach for `unordered-delta`
//! and a [`MapDelta`](crate::MapDelta) when the values are big enough to be
//! worth diffing and implement [`Delta`](crate::Delta) so they can be.
//!
//! The derive emits calls to [`diff`] and [`apply`]; you only need this module
//! directly to inspect or construct a delta by hand.
use crate::;
/// A membership diff between two collections of key/value entries.
///
/// The two parts are asymmetric, and the map's one-value-per-key rule is what
/// makes them so: [`add`] carries whole entries because the receiver needs to
/// be told the value, while [`remove`] carries bare keys because a key names
/// an entry on its own.
///
/// A key that survived with a new value under it is an [`add`], not a removal
/// followed by one. Applying an addition overwrites whatever the key held, so
/// the removal would say nothing the addition does not already say.
///
/// Nothing here records position — see [`SeqDelta`](crate::SeqDelta) for that.
///
/// [`add`]: EntryDelta::add
/// [`remove`]: EntryDelta::remove
/// Pairs the entries of `old` and `new` by key and records what the keys that
/// survived hold now.
///
/// Returns an empty [`EntryDelta`] when the two hold the same entries. 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::entry::diff;
/// use std::collections::BTreeMap;
///
/// let labels = |tier: &'static str| {
/// vec![("tier", tier)].into_iter().collect::<BTreeMap<&str, &str>>()
/// };
///
/// let delta = diff(labels("web"), labels("edge"));
/// // The key survived, so only what it holds now travels — the old value
/// // stays where it is, on the receiver.
/// assert_eq!(delta.add, vec![("tier", "edge")]);
/// assert!(delta.remove.is_empty());
/// ```
/// Applies a keyed membership diff to `target` in place.
///
/// Each removal is a single lookup rather than a scan, so this costs the same
/// as [`diff`] does. Membership is preserved but position is not — additions
/// land wherever the collection decides to put them. Use `ordered` where that
/// matters.
///
/// A removal naming a key that `target` does not have is ignored, and an
/// addition overwrites whatever the key held, which together make applying the
/// same delta twice harmless.
///
/// ```
/// use delta_struct::entry::{apply, diff};
/// use std::collections::BTreeMap;
///
/// let labels = |entries: Vec<(&'static str, &'static str)>| {
/// entries.into_iter().collect::<BTreeMap<&str, &str>>()
/// };
///
/// let delta = diff(
/// labels(vec![("tier", "web"), ("zone", "a")]),
/// labels(vec![("tier", "edge")]),
/// );
/// let mut target = labels(vec![("tier", "web"), ("zone", "a")]);
/// apply(&mut target, delta);
/// assert_eq!(target, labels(vec![("tier", "edge")]));
/// ```