///|
/// Merge two manifests while keeping the newer snapshot for duplicate paths.
/// The operation is deterministic: the left manifest's order is retained and
/// new paths from the right manifest are appended in their original order.
pub fn Manifest::merge(self : Manifest, other : Manifest) -> Manifest {
let merged : Array[FileSnapshot] = []
for file in self.files {
merged.push(file)
}
for incoming in other.files {
let mut replaced = false
for i = 0; i < merged.length(); i = i + 1 {
if merged[i].path == incoming.path {
merged[i] = incoming
replaced = true
break
}
}
if !replaced {
merged.push(incoming)
}
}
Manifest::new(self.name, other.version, other.created_at, merged)
}
///|
/// Return a manifest containing only changed files from a diff.
pub fn ManifestDiff::changed_files(self : ManifestDiff) -> Array[FileSnapshot] {
let result : Array[FileSnapshot] = []
for file in self.added {
result.push(file)
}
for file in self.modified {
result.push(file)
}
result
}
///|
/// Return true when two snapshots have identical cryptographic content.
pub fn snapshots_equal(left : FileSnapshot, right : FileSnapshot) -> Bool {
left.size == right.size && left.hash_sha256 == right.hash_sha256
}
///|
/// Return true when every file in the manifest has a valid SHA-256 record.
pub fn Manifest::has_complete_digests(self : Manifest) -> Bool {
for file in self.files {
if file.hash_sha256.length() != 64 {
return false
}
}
true
}
///|
/// Return all paths in a diff that were removed from the newer release.
pub fn ManifestDiff::removed_paths(self : ManifestDiff) -> Array[String] {
let result : Array[String] = []
for file in self.removed {
result.push(file.path)
}
result
}
///|
/// Validate that all snapshot paths are unique without creating a full report.
pub fn Manifest::has_unique_paths(self : Manifest) -> Bool {
for i = 0; i < self.files.length(); i = i + 1 {
for j = i + 1; j < self.files.length(); j = j + 1 {
if self.files[i].path == self.files[j].path {
return false
}
}
}
true
}