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
//! Membership diffing, behind the `unordered` field type when the field is a
//! set.
//!
//! A set marked `#[delta_struct(field_type = "unordered")]` is treated as a
//! bag of elements whose order carries no meaning, and represented as a
//! [`BagDelta`] — which elements came and which went, and nothing about where
//! they sit.
//!
//! A map marked the same way gets an [`EntryDelta`](crate::EntryDelta)
//! instead, since it can name a departing entry by key alone; see
//! [`entry`](crate::entry). [`Unordered`](crate::Unordered) is what picks
//! between the two.
//!
//! The derive emits calls to [`diff`] and [`apply`]; you only need this module
//! directly to inspect or construct a delta by hand.
use crateTryIndex;
/// A membership diff between two collections: what arrived and what left.
///
/// Nothing here records position — see [`SeqDelta`](crate::SeqDelta) for that.
/// Computes which elements `new` gained and which `old` lost.
///
/// Returns an empty [`BagDelta`] when the two hold the same elements. Each
/// element 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 [`HashSet`](std::collections::HashSet), O(n log n) for
/// a [`BTreeSet`](std::collections::BTreeSet).
///
/// ```
/// use delta_struct::bag::diff;
/// use std::collections::BTreeSet;
///
/// let old: BTreeSet<i32> = vec![1, 2, 3].into_iter().collect();
/// let new: BTreeSet<i32> = vec![3, 4, 5].into_iter().collect();
///
/// let delta = diff(old, new);
/// assert_eq!(delta.add, vec![4, 5]);
/// assert_eq!(delta.remove, vec![1, 2]);
/// ```
/// Applies a 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 that `target` does not have is ignored, which makes applying the
/// same delta twice harmless.
///
/// ```
/// use delta_struct::bag::{apply, diff};
/// use std::collections::BTreeSet;
///
/// let set = |items: Vec<i32>| items.into_iter().collect::<BTreeSet<i32>>();
///
/// let delta = diff(set(vec![1, 2, 3]), set(vec![2, 3, 4]));
/// let mut target = set(vec![1, 2, 3]);
/// apply(&mut target, delta);
/// assert_eq!(target, set(vec![2, 3, 4]));
/// ```