///|
fn check_inventory(view : BagView, diagnostics : Array[Diagnostic]) -> Unit {
let directories : Map[String, Bool] = Map([])
let files : Map[String, Bool] = Map([])
for file in view.files {
files[file.path] = true
}
for path, _ in view.texts {
if !files.contains(path) {
issue(
diagnostics, "orphan_text", path, "decoded text has no inventory file record",
)
}
}
for directory in view.directories {
check_path(directory) catch {
Invalid(message) =>
issue(diagnostics, "unsafe_directory", directory, message)
}
if directories.contains(directory) {
issue(
diagnostics, "duplicate_directory", directory, "duplicate inventory directory",
)
}
directories[directory] = true
if files.contains(directory) {
issue(
diagnostics, "file_directory_collision", directory, "inventory path is both a file and a directory",
)
}
if directory == "bagit.txt" ||
directory == "bag-info.txt" ||
directory == "fetch.txt" ||
manifest_name(directory, false) ||
manifest_name(directory, true) {
issue(
diagnostics, "reserved_tag_directory", directory, "standard tag filename is occupied by a directory",
)
}
}
if files.contains("data") {
issue(
diagnostics, "payload_root_file", "data", "payload root must be a directory, not a file",
)
}
let paths = view.directories.copy()
paths.append(view.files.map(f => f.path))
let checked : Map[String, Bool] = Map([])
// Each parent is checked once even for a large flat inventory.
for path in paths {
let mut end = path.length()
while path[:end].to_owned().rev_find("/") is Some(slash) {
let parent = path[:slash].to_owned()
if checked.contains(parent) {
break
}
checked[parent] = true
if !directories.contains(parent) {
issue(
diagnostics, "missing_parent_directory", parent, "parent directory is missing from inventory",
)
}
if files.contains(parent) {
issue(
diagnostics, "ancestor_file", parent, "a file cannot be an ancestor of another entry",
)
}
end = slash
}
}
}