Skip to main content

iddqd/id_hash_map/
daft_impls.rs

1//! `Diffable` implementation.
2
3use super::{IdHashItem, IdHashMap};
4use crate::{
5    DefaultHashBuilder,
6    support::{
7        alloc::{Allocator, Global},
8        daft_utils::IdLeaf,
9    },
10};
11use core::{
12    fmt,
13    hash::{BuildHasher, Hash},
14};
15use daft::Diffable;
16use equivalent::Equivalent;
17
18impl<T: IdHashItem, S: Clone + BuildHasher, A: Clone + Allocator> Diffable
19    for IdHashMap<T, S, A>
20{
21    type Diff<'a>
22        = Diff<'a, T, S, A>
23    where
24        T: 'a,
25        S: 'a,
26        A: 'a;
27
28    fn diff<'daft>(&'daft self, other: &'daft Self) -> Self::Diff<'daft> {
29        let mut diff = Diff::with_hasher_in(
30            self.hasher().clone(),
31            self.allocator().clone(),
32        );
33        for item in self {
34            if let Some(other_item) = other.get(&item.key()) {
35                diff.common.insert_overwrite(IdLeaf::new(item, other_item));
36            } else {
37                diff.removed.insert_overwrite(item);
38            }
39        }
40        for item in other {
41            if !self.contains_key(&item.key()) {
42                diff.added.insert_overwrite(item);
43            }
44        }
45        diff
46    }
47}
48
49/// A diff of two [`IdHashMap`]s.
50///
51/// Generated by the [`Diffable`] implementation for [`IdHashMap`].
52///
53/// # Examples
54///
55/// ```
56/// # #[cfg(feature = "default-hasher")] {
57/// use daft::Diffable;
58/// use iddqd::{IdHashItem, IdHashMap, id_upcast};
59///
60/// #[derive(Eq, PartialEq)]
61/// struct Item {
62///     id: String,
63///     value: u32,
64/// }
65///
66/// impl IdHashItem for Item {
67///     type Key<'a> = &'a str;
68///     fn key(&self) -> Self::Key<'_> {
69///         &self.id
70///     }
71///     id_upcast!();
72/// }
73///
74/// // Create two IdHashMaps with overlapping items.
75/// let mut map1 = IdHashMap::new();
76/// map1.insert_unique(Item { id: "a".to_string(), value: 1 });
77/// map1.insert_unique(Item { id: "b".to_string(), value: 2 });
78///
79/// let mut map2 = IdHashMap::new();
80/// map2.insert_unique(Item { id: "b".to_string(), value: 3 });
81/// map2.insert_unique(Item { id: "c".to_string(), value: 4 });
82///
83/// // Compute the diff between the two maps.
84/// let diff = map1.diff(&map2);
85///
86/// // "a" is removed.
87/// assert!(diff.removed.contains_key("a"));
88/// // "b" is modified (value changed from 2 to 3).
89/// assert!(diff.is_modified("b"));
90/// // "c" is added.
91/// assert!(diff.added.contains_key("c"));
92/// # }
93/// ```
94///
95/// [`Diffable`]: daft::Diffable
96pub struct Diff<
97    'daft,
98    T: ?Sized + IdHashItem,
99    S = DefaultHashBuilder,
100    A: Allocator = Global,
101> {
102    /// Entries common to both maps.
103    ///
104    /// Items are stored as [`IdLeaf`]s to references.
105    pub common: IdHashMap<IdLeaf<&'daft T>, S, A>,
106
107    /// Added entries.
108    pub added: IdHashMap<&'daft T, S, A>,
109
110    /// Removed entries.
111    pub removed: IdHashMap<&'daft T, S, A>,
112}
113
114impl<'a, 'daft, T, S: Clone + BuildHasher, A: Allocator> fmt::Debug
115    for Diff<'daft, T, S, A>
116where
117    T: ?Sized + IdHashItem + fmt::Debug,
118    T::Key<'a>: fmt::Debug,
119    T: 'a,
120    'daft: 'a,
121{
122    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123        f.debug_struct("Diff")
124            .field("common", &self.common)
125            .field("added", &self.added)
126            .field("removed", &self.removed)
127            .finish()
128    }
129}
130
131impl<'daft, T: ?Sized + IdHashItem, S: Default, A: Allocator + Default> Default
132    for Diff<'daft, T, S, A>
133{
134    fn default() -> Self {
135        Self {
136            common: IdHashMap::default(),
137            added: IdHashMap::default(),
138            removed: IdHashMap::default(),
139        }
140    }
141}
142
143#[cfg(all(feature = "default-hasher", feature = "allocator-api2"))]
144impl<'daft, T: ?Sized + IdHashItem> Diff<'daft, T> {
145    /// Creates a new, empty `Diff`.
146    pub fn new() -> Self {
147        Self {
148            common: IdHashMap::new(),
149            added: IdHashMap::new(),
150            removed: IdHashMap::new(),
151        }
152    }
153}
154
155#[cfg(feature = "allocator-api2")]
156impl<'daft, T: ?Sized + IdHashItem, S: Clone + BuildHasher> Diff<'daft, T, S> {
157    /// Creates a new, empty `Diff` with the given hasher.
158    pub fn with_hasher(hasher: S) -> Self {
159        Self {
160            common: IdHashMap::with_hasher(hasher.clone()),
161            added: IdHashMap::with_hasher(hasher.clone()),
162            removed: IdHashMap::with_hasher(hasher),
163        }
164    }
165}
166
167impl<
168    'daft,
169    T: ?Sized + IdHashItem,
170    S: Clone + BuildHasher,
171    A: Clone + Allocator,
172> Diff<'daft, T, S, A>
173{
174    /// Creates a new, empty `Diff` with the given hasher and allocator.
175    pub fn with_hasher_in(hasher: S, alloc: A) -> Self {
176        Self {
177            common: IdHashMap::with_hasher_in(hasher.clone(), alloc.clone()),
178            added: IdHashMap::with_hasher_in(hasher.clone(), alloc.clone()),
179            removed: IdHashMap::with_hasher_in(hasher, alloc),
180        }
181    }
182}
183
184impl<'daft, T: ?Sized + IdHashItem + Eq, S: Clone + BuildHasher, A: Allocator>
185    Diff<'daft, T, S, A>
186{
187    /// Returns an iterator over unchanged keys and values.
188    pub fn unchanged(&self) -> impl Iterator<Item = &'daft T> + '_ {
189        self.common
190            .iter()
191            .filter_map(|leaf| leaf.is_unchanged().then_some(*leaf.before()))
192    }
193
194    /// Returns true if the item corresponding to the key is unchanged.
195    pub fn is_unchanged<'a, Q>(&'a self, key: &Q) -> bool
196    where
197        Q: ?Sized + Hash + Equivalent<T::Key<'a>>,
198    {
199        self.common.get(key).is_some_and(|leaf| leaf.is_unchanged())
200    }
201
202    /// Returns the value associated with the key if it is unchanged,
203    /// otherwise `None`.
204    pub fn get_unchanged<'a, Q>(&'a self, key: &Q) -> Option<&'daft T>
205    where
206        Q: ?Sized + Hash + Equivalent<T::Key<'a>>,
207    {
208        self.common
209            .get(key)
210            .and_then(|leaf| leaf.is_unchanged().then_some(*leaf.before()))
211    }
212
213    /// Returns an iterator over modified keys and values.
214    pub fn modified(&self) -> impl Iterator<Item = IdLeaf<&'daft T>> + '_ {
215        self.common
216            .iter()
217            .filter_map(|leaf| leaf.is_modified().then_some(*leaf))
218    }
219
220    /// Returns true if the value corresponding to the key is
221    /// modified.
222    pub fn is_modified<'a, Q>(&'a self, key: &Q) -> bool
223    where
224        Q: ?Sized + Hash + Equivalent<T::Key<'a>>,
225    {
226        self.common.get(key).is_some_and(|leaf| leaf.is_modified())
227    }
228
229    /// Returns the [`IdLeaf`] associated with the key if it is modified,
230    /// otherwise `None`.
231    pub fn get_modified<'a, Q>(&'a self, key: &Q) -> Option<IdLeaf<&'daft T>>
232    where
233        Q: ?Sized + Hash + Equivalent<T::Key<'a>>,
234    {
235        self.common
236            .get(key)
237            .and_then(|leaf| leaf.is_modified().then_some(*leaf))
238    }
239
240    /// Returns an iterator over modified keys and values, performing
241    /// a diff on the values.
242    ///
243    /// This is useful when `T::Diff` is a complex type, not just a
244    /// [`daft::Leaf`].
245    pub fn modified_diff(&self) -> impl Iterator<Item = T::Diff<'daft>> + '_
246    where
247        T: Diffable,
248    {
249        self.modified().map(|leaf| leaf.diff_pair())
250    }
251}
252
253impl<T: IdHashItem> IdHashItem for IdLeaf<T> {
254    type Key<'a>
255        = T::Key<'a>
256    where
257        T: 'a;
258
259    fn key(&self) -> Self::Key<'_> {
260        let before_key = self.before().key();
261        if before_key != self.after().key() {
262            panic!("key is different between before and after");
263        }
264        before_key
265    }
266
267    #[inline]
268    fn upcast_key<'short, 'long: 'short>(
269        long: Self::Key<'long>,
270    ) -> Self::Key<'short> {
271        T::upcast_key(long)
272    }
273}