1use foldhash::HashMap;
2use foldhash::HashSet;
3use serde::Deserialize;
4use serde::Serialize;
5
6use mago_database::file::FileId;
7
8use crate::differ::compute_file_diff;
9use crate::metadata::CodebaseMetadata;
10use crate::symbol::SymbolIdentifier;
11
12pub type DiffHunk = (usize, usize, isize, isize);
20
21pub type DeletionRange = (usize, usize);
27
28#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
35pub struct CodebaseDiff {
36 keep: HashSet<SymbolIdentifier>,
40
41 changed: HashSet<SymbolIdentifier>,
45
46 diff_map: HashMap<FileId, Vec<DiffHunk>>,
49
50 deletion_ranges_map: HashMap<FileId, Vec<DeletionRange>>,
53}
54
55impl CodebaseDiff {
56 #[inline]
57 #[must_use]
58 pub fn new() -> Self {
59 Self::default()
60 }
61
62 pub fn between(old_metadata: &CodebaseMetadata, new_metadata: &CodebaseMetadata) -> Self {
69 let mut aggregate_diff = CodebaseDiff::new();
70
71 let mut all_file_ids = old_metadata.get_all_file_ids();
72 all_file_ids.extend(new_metadata.get_all_file_ids());
73 all_file_ids.sort();
74 all_file_ids.dedup();
75
76 for file_id in all_file_ids {
77 let old_sig = old_metadata.get_file_signature(&file_id);
78 let new_sig = new_metadata.get_file_signature(&file_id);
79
80 let file_diff = compute_file_diff(file_id, old_sig, new_sig);
81
82 aggregate_diff.extend(file_diff);
83 }
84
85 aggregate_diff
86 }
87
88 #[inline]
90 pub fn extend(&mut self, other: Self) {
91 self.keep.extend(other.keep);
92 self.changed.extend(other.changed);
93 for (source, diffs) in other.diff_map {
94 self.diff_map.entry(source).or_default().extend(diffs);
95 }
96 for (source, ranges) in other.deletion_ranges_map {
97 self.deletion_ranges_map.entry(source).or_default().extend(ranges);
98 }
99 }
100
101 #[inline]
103 #[must_use]
104 pub fn get_keep(&self) -> &HashSet<SymbolIdentifier> {
105 &self.keep
106 }
107
108 #[inline]
110 #[must_use]
111 pub fn get_changed(&self) -> &HashSet<SymbolIdentifier> {
112 &self.changed
113 }
114
115 #[inline]
117 #[must_use]
118 pub fn get_diff_map(&self) -> &HashMap<FileId, Vec<DiffHunk>> {
119 &self.diff_map
120 }
121
122 #[inline]
124 #[must_use]
125 pub fn get_deletion_ranges_map(&self) -> &HashMap<FileId, Vec<DeletionRange>> {
126 &self.deletion_ranges_map
127 }
128
129 #[inline]
131 pub fn set_keep(&mut self, keep_set: impl IntoIterator<Item = SymbolIdentifier>) {
132 self.keep = keep_set.into_iter().collect();
133 }
134
135 #[inline]
137 pub fn with_keep(mut self, keep_set: impl IntoIterator<Item = SymbolIdentifier>) -> Self {
138 self.set_keep(keep_set);
139 self
140 }
141
142 #[inline]
144 pub fn add_keep_entry(&mut self, entry: SymbolIdentifier) -> bool {
145 self.keep.insert(entry)
146 }
147
148 #[inline]
150 #[must_use]
151 pub fn with_added_keep_entry(mut self, entry: SymbolIdentifier) -> Self {
152 self.add_keep_entry(entry);
153 self
154 }
155
156 #[inline]
158 pub fn add_keep_entries(&mut self, entries: impl IntoIterator<Item = SymbolIdentifier>) {
159 self.keep.extend(entries);
160 }
161
162 #[inline]
164 pub fn with_added_keep_entries(mut self, entries: impl IntoIterator<Item = SymbolIdentifier>) -> Self {
165 self.add_keep_entries(entries);
166 self
167 }
168
169 #[inline]
171 pub fn unset_keep(&mut self) {
172 self.keep.clear();
173 }
174
175 #[inline]
177 #[must_use]
178 pub fn without_keep(mut self) -> Self {
179 self.unset_keep();
180 self
181 }
182
183 #[inline]
185 pub fn set_changed(&mut self, change_set: impl IntoIterator<Item = SymbolIdentifier>) {
186 self.changed = change_set.into_iter().collect();
187 }
188
189 #[inline]
191 pub fn with_changed(mut self, change_set: impl IntoIterator<Item = SymbolIdentifier>) -> Self {
192 self.set_changed(change_set);
193 self
194 }
195
196 #[inline]
198 pub fn add_changed_entry(&mut self, entry: SymbolIdentifier) -> bool {
199 self.changed.insert(entry)
200 }
201
202 #[inline]
204 #[must_use]
205 pub fn contains_changed_entry(&self, entry: &SymbolIdentifier) -> bool {
206 self.changed.contains(entry)
207 }
208
209 #[inline]
211 #[must_use]
212 pub fn with_added_changed_entry(mut self, entry: SymbolIdentifier) -> Self {
213 self.add_changed_entry(entry);
214 self
215 }
216
217 #[inline]
219 pub fn add_changed_entries(&mut self, entries: impl IntoIterator<Item = SymbolIdentifier>) {
220 self.changed.extend(entries);
221 }
222
223 #[inline]
225 pub fn with_added_changed_entries(mut self, entries: impl IntoIterator<Item = SymbolIdentifier>) -> Self {
226 self.add_changed_entries(entries);
227 self
228 }
229
230 #[inline]
232 pub fn unset_changed(&mut self) {
233 self.changed.clear();
234 }
235
236 #[inline]
238 #[must_use]
239 pub fn without_changed(mut self) -> Self {
240 self.unset_changed();
241 self
242 }
243
244 #[inline]
246 pub fn set_diff_map(&mut self, map: HashMap<FileId, Vec<DiffHunk>>) {
247 self.diff_map = map;
248 }
249
250 #[inline]
252 #[must_use]
253 pub fn with_diff_map(mut self, map: HashMap<FileId, Vec<DiffHunk>>) -> Self {
254 self.set_diff_map(map);
255 self
256 }
257
258 #[inline]
260 pub fn add_diff_map_entry(&mut self, source: FileId, diffs: Vec<DiffHunk>) -> Option<Vec<DiffHunk>> {
261 self.diff_map.insert(source, diffs)
262 }
263
264 #[inline]
266 #[must_use]
267 pub fn with_added_diff_map_entry(mut self, source: FileId, diffs: Vec<DiffHunk>) -> Self {
268 self.add_diff_map_entry(source, diffs);
269 self
270 }
271
272 #[inline]
274 pub fn add_diffs_for_source(&mut self, source: FileId, diffs: impl IntoIterator<Item = DiffHunk>) {
275 self.diff_map.entry(source).or_default().extend(diffs);
276 }
277
278 #[inline]
280 pub fn with_added_diffs_for_source(mut self, source: FileId, diffs: impl IntoIterator<Item = DiffHunk>) -> Self {
281 self.add_diffs_for_source(source, diffs);
282 self
283 }
284
285 #[inline]
287 pub fn unset_diff_map(&mut self) {
288 self.diff_map.clear();
289 }
290
291 #[inline]
293 #[must_use]
294 pub fn without_diff_map(mut self) -> Self {
295 self.unset_diff_map();
296 self
297 }
298
299 #[inline]
301 pub fn set_deletion_ranges_map(&mut self, map: HashMap<FileId, Vec<DeletionRange>>) {
302 self.deletion_ranges_map = map;
303 }
304
305 #[inline]
307 #[must_use]
308 pub fn with_deletion_ranges_map(mut self, map: HashMap<FileId, Vec<DeletionRange>>) -> Self {
309 self.set_deletion_ranges_map(map);
310 self
311 }
312
313 #[inline]
315 pub fn add_deletion_ranges_entry(
316 &mut self,
317 source: FileId,
318 ranges: Vec<DeletionRange>,
319 ) -> Option<Vec<DeletionRange>> {
320 self.deletion_ranges_map.insert(source, ranges)
321 }
322
323 #[inline]
325 #[must_use]
326 pub fn with_added_deletion_ranges_entry(mut self, file: FileId, ranges: Vec<DeletionRange>) -> Self {
327 self.add_deletion_ranges_entry(file, ranges);
328 self
329 }
330
331 #[inline]
333 pub fn add_deletion_ranges_for_source(&mut self, file: FileId, ranges: impl IntoIterator<Item = (usize, usize)>) {
334 self.deletion_ranges_map.entry(file).or_default().extend(ranges);
335 }
336
337 #[inline]
339 pub fn with_added_deletion_ranges_for_source(
340 mut self,
341 file: FileId,
342 ranges: impl IntoIterator<Item = (usize, usize)>,
343 ) -> Self {
344 self.add_deletion_ranges_for_source(file, ranges);
345 self
346 }
347
348 #[inline]
350 pub fn unset_deletion_ranges_map(&mut self) {
351 self.deletion_ranges_map.clear();
352 }
353
354 #[inline]
356 #[must_use]
357 pub fn without_deletion_ranges_map(mut self) -> Self {
358 self.unset_deletion_ranges_map();
359 self
360 }
361}