use foldhash::HashMap;
use foldhash::HashSet;
use serde::Deserialize;
use serde::Serialize;
use mago_database::file::FileId;
use crate::differ::compute_file_diff;
use crate::metadata::CodebaseMetadata;
use crate::symbol::SymbolIdentifier;
pub type DiffHunk = (usize, usize, isize, isize);
pub type DeletionRange = (usize, usize);
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CodebaseDiff {
keep: HashSet<SymbolIdentifier>,
changed: HashSet<SymbolIdentifier>,
diff_map: HashMap<FileId, Vec<DiffHunk>>,
deletion_ranges_map: HashMap<FileId, Vec<DeletionRange>>,
}
impl CodebaseDiff {
#[inline]
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn between(old_metadata: &CodebaseMetadata, new_metadata: &CodebaseMetadata) -> Self {
let mut aggregate_diff = CodebaseDiff::new();
let mut all_file_ids = old_metadata.get_all_file_ids();
all_file_ids.extend(new_metadata.get_all_file_ids());
all_file_ids.sort();
all_file_ids.dedup();
for file_id in all_file_ids {
let old_sig = old_metadata.get_file_signature(&file_id);
let new_sig = new_metadata.get_file_signature(&file_id);
let file_diff = compute_file_diff(file_id, old_sig, new_sig);
aggregate_diff.extend(file_diff);
}
aggregate_diff
}
#[inline]
pub fn extend(&mut self, other: Self) {
self.keep.extend(other.keep);
self.changed.extend(other.changed);
for (source, diffs) in other.diff_map {
self.diff_map.entry(source).or_default().extend(diffs);
}
for (source, ranges) in other.deletion_ranges_map {
self.deletion_ranges_map.entry(source).or_default().extend(ranges);
}
}
#[inline]
#[must_use]
pub fn get_keep(&self) -> &HashSet<SymbolIdentifier> {
&self.keep
}
#[inline]
#[must_use]
pub fn get_changed(&self) -> &HashSet<SymbolIdentifier> {
&self.changed
}
#[inline]
#[must_use]
pub fn get_diff_map(&self) -> &HashMap<FileId, Vec<DiffHunk>> {
&self.diff_map
}
#[inline]
#[must_use]
pub fn get_deletion_ranges_map(&self) -> &HashMap<FileId, Vec<DeletionRange>> {
&self.deletion_ranges_map
}
#[inline]
pub fn set_keep(&mut self, keep_set: impl IntoIterator<Item = SymbolIdentifier>) {
self.keep = keep_set.into_iter().collect();
}
#[inline]
pub fn with_keep(mut self, keep_set: impl IntoIterator<Item = SymbolIdentifier>) -> Self {
self.set_keep(keep_set);
self
}
#[inline]
pub fn add_keep_entry(&mut self, entry: SymbolIdentifier) -> bool {
self.keep.insert(entry)
}
#[inline]
#[must_use]
pub fn with_added_keep_entry(mut self, entry: SymbolIdentifier) -> Self {
self.add_keep_entry(entry);
self
}
#[inline]
pub fn add_keep_entries(&mut self, entries: impl IntoIterator<Item = SymbolIdentifier>) {
self.keep.extend(entries);
}
#[inline]
pub fn with_added_keep_entries(mut self, entries: impl IntoIterator<Item = SymbolIdentifier>) -> Self {
self.add_keep_entries(entries);
self
}
#[inline]
pub fn unset_keep(&mut self) {
self.keep.clear();
}
#[inline]
#[must_use]
pub fn without_keep(mut self) -> Self {
self.unset_keep();
self
}
#[inline]
pub fn set_changed(&mut self, change_set: impl IntoIterator<Item = SymbolIdentifier>) {
self.changed = change_set.into_iter().collect();
}
#[inline]
pub fn with_changed(mut self, change_set: impl IntoIterator<Item = SymbolIdentifier>) -> Self {
self.set_changed(change_set);
self
}
#[inline]
pub fn add_changed_entry(&mut self, entry: SymbolIdentifier) -> bool {
self.changed.insert(entry)
}
#[inline]
#[must_use]
pub fn contains_changed_entry(&self, entry: &SymbolIdentifier) -> bool {
self.changed.contains(entry)
}
#[inline]
#[must_use]
pub fn with_added_changed_entry(mut self, entry: SymbolIdentifier) -> Self {
self.add_changed_entry(entry);
self
}
#[inline]
pub fn add_changed_entries(&mut self, entries: impl IntoIterator<Item = SymbolIdentifier>) {
self.changed.extend(entries);
}
#[inline]
pub fn with_added_changed_entries(mut self, entries: impl IntoIterator<Item = SymbolIdentifier>) -> Self {
self.add_changed_entries(entries);
self
}
#[inline]
pub fn unset_changed(&mut self) {
self.changed.clear();
}
#[inline]
#[must_use]
pub fn without_changed(mut self) -> Self {
self.unset_changed();
self
}
#[inline]
pub fn set_diff_map(&mut self, map: HashMap<FileId, Vec<DiffHunk>>) {
self.diff_map = map;
}
#[inline]
#[must_use]
pub fn with_diff_map(mut self, map: HashMap<FileId, Vec<DiffHunk>>) -> Self {
self.set_diff_map(map);
self
}
#[inline]
pub fn add_diff_map_entry(&mut self, source: FileId, diffs: Vec<DiffHunk>) -> Option<Vec<DiffHunk>> {
self.diff_map.insert(source, diffs)
}
#[inline]
#[must_use]
pub fn with_added_diff_map_entry(mut self, source: FileId, diffs: Vec<DiffHunk>) -> Self {
self.add_diff_map_entry(source, diffs);
self
}
#[inline]
pub fn add_diffs_for_source(&mut self, source: FileId, diffs: impl IntoIterator<Item = DiffHunk>) {
self.diff_map.entry(source).or_default().extend(diffs);
}
#[inline]
pub fn with_added_diffs_for_source(mut self, source: FileId, diffs: impl IntoIterator<Item = DiffHunk>) -> Self {
self.add_diffs_for_source(source, diffs);
self
}
#[inline]
pub fn unset_diff_map(&mut self) {
self.diff_map.clear();
}
#[inline]
#[must_use]
pub fn without_diff_map(mut self) -> Self {
self.unset_diff_map();
self
}
#[inline]
pub fn set_deletion_ranges_map(&mut self, map: HashMap<FileId, Vec<DeletionRange>>) {
self.deletion_ranges_map = map;
}
#[inline]
#[must_use]
pub fn with_deletion_ranges_map(mut self, map: HashMap<FileId, Vec<DeletionRange>>) -> Self {
self.set_deletion_ranges_map(map);
self
}
#[inline]
pub fn add_deletion_ranges_entry(
&mut self,
source: FileId,
ranges: Vec<DeletionRange>,
) -> Option<Vec<DeletionRange>> {
self.deletion_ranges_map.insert(source, ranges)
}
#[inline]
#[must_use]
pub fn with_added_deletion_ranges_entry(mut self, file: FileId, ranges: Vec<DeletionRange>) -> Self {
self.add_deletion_ranges_entry(file, ranges);
self
}
#[inline]
pub fn add_deletion_ranges_for_source(&mut self, file: FileId, ranges: impl IntoIterator<Item = (usize, usize)>) {
self.deletion_ranges_map.entry(file).or_default().extend(ranges);
}
#[inline]
pub fn with_added_deletion_ranges_for_source(
mut self,
file: FileId,
ranges: impl IntoIterator<Item = (usize, usize)>,
) -> Self {
self.add_deletion_ranges_for_source(file, ranges);
self
}
#[inline]
pub fn unset_deletion_ranges_map(&mut self) {
self.deletion_ranges_map.clear();
}
#[inline]
#[must_use]
pub fn without_deletion_ranges_map(mut self) -> Self {
self.unset_deletion_ranges_map();
self
}
}