1use 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
49pub struct Diff<
97 'daft,
98 T: ?Sized + IdHashItem,
99 S = DefaultHashBuilder,
100 A: Allocator = Global,
101> {
102 pub common: IdHashMap<IdLeaf<&'daft T>, S, A>,
106
107 pub added: IdHashMap<&'daft T, S, A>,
109
110 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 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 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 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 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 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 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 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 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 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 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}