///|
pub(all) enum ChangeKind {
Added
Removed
Grew
Shrank
Modified
KindChanged
} derive(Eq, Debug)
///|
pub(all) struct EntryChange {
path : String
kind : ChangeKind
before : Entry?
after : Entry?
size_delta : Int64
} derive(Eq, Debug)
///|
pub(all) struct DirectoryChange {
path : String
before_size : Int64
after_size : Int64
size_delta : Int64
} derive(Eq, Debug)
///|
pub(all) struct SnapshotDiff {
changes : Array[EntryChange]
directories : Array[DirectoryChange]
added_size : Int64
removed_size : Int64
net_size : Int64
} derive(Eq, Debug)
///|
fn entry_map(snapshot : Snapshot) -> Map[String, Entry] {
let result : Map[String, Entry] = Map([])
for entry in snapshot.entries {
result[entry.path] = entry
}
result
}
///|
fn classify_existing(before : Entry, after : Entry) -> ChangeKind? {
if before.kind != after.kind {
Some(KindChanged)
} else if before.size < after.size {
Some(Grew)
} else if before.size > after.size {
Some(Shrank)
} else if before.modified_at != after.modified_at {
Some(Modified)
} else {
None
}
}
///|
fn directory_size_map(snapshot : Snapshot) -> Map[String, Int64] {
let result : Map[String, Int64] = Map([])
for stat in directory_stats(snapshot) {
result[stat.path] = stat.recursive_size
}
result
}
///|
pub fn compare_snapshots(before : Snapshot, after : Snapshot) -> SnapshotDiff {
let old = entry_map(before)
let current = entry_map(after)
let changes : Array[EntryChange] = []
let mut added_size = 0L
let mut removed_size = 0L
for path, previous in old {
match current.get(path) {
None => {
let size = if previous.is_file() { previous.size } else { 0L }
removed_size += size
changes.push(EntryChange::{
path,
kind: Removed,
before: Some(previous),
after: None,
size_delta: -size,
})
}
Some(now) =>
match classify_existing(previous, now) {
Some(kind) =>
changes.push(EntryChange::{
path,
kind,
before: Some(previous),
after: Some(now),
size_delta: now.size - previous.size,
})
None => ()
}
}
}
for path, now in current {
if old.get(path) is None {
let size = if now.is_file() { now.size } else { 0L }
added_size += size
changes.push(EntryChange::{
path,
kind: Added,
before: None,
after: Some(now),
size_delta: size,
})
}
}
changes.sort_by(fn(a, b) {
let aa = if a.size_delta < 0L { -a.size_delta } else { a.size_delta }
let bb = if b.size_delta < 0L { -b.size_delta } else { b.size_delta }
if aa > bb {
-1
} else if aa < bb {
1
} else if a.path < b.path {
-1
} else {
1
}
})
let old_dirs = directory_size_map(before)
let new_dirs = directory_size_map(after)
let all_dirs : Map[String, Bool] = Map([])
for path, _ in old_dirs {
all_dirs[path] = true
}
for path, _ in new_dirs {
all_dirs[path] = true
}
let directories : Array[DirectoryChange] = []
for path, _ in all_dirs {
let old_size = old_dirs.get(path).unwrap_or(0L)
let new_size = new_dirs.get(path).unwrap_or(0L)
if old_size != new_size {
directories.push(DirectoryChange::{
path,
before_size: old_size,
after_size: new_size,
size_delta: new_size - old_size,
})
}
}
directories.sort_by(fn(a, b) {
if a.size_delta > b.size_delta {
-1
} else if a.size_delta < b.size_delta {
1
} else if a.path < b.path {
-1
} else {
1
}
})
SnapshotDiff::{
changes,
directories,
added_size,
removed_size,
net_size: after.total_size() - before.total_size(),
}
}
///|
pub fn SnapshotDiff::added_count(self : SnapshotDiff) -> Int {
self.changes.filter(fn(change) { change.kind == Added }).length()
}
///|
pub fn SnapshotDiff::removed_count(self : SnapshotDiff) -> Int {
self.changes.filter(fn(change) { change.kind == Removed }).length()
}
///|
pub fn SnapshotDiff::changed_count(self : SnapshotDiff) -> Int {
self.changes.length() - self.added_count() - self.removed_count()
}