///|
fn any_rule(rules : Array[String], path : String, ancestors : Bool) -> Bool {
let mut prefix = path
while true {
for rule in rules {
if @glob.matches(rule, prefix) == Ok(true) {
return true
}
}
if !ancestors {
return false
}
let parts = prefix.split("/").map(x => x.to_owned()).to_array()
if parts.length() <= 1 {
return false
}
let _ = parts.pop()
prefix = parts.join("/")
} nobreak {
false
}
}
///|
fn protected_by_default(path : String) -> Bool {
let parts = path.to_lower().split("/").map(x => x.to_owned()).to_array()
for part in parts {
if part == "scripts" ||
part == ".git" ||
part == ".github" ||
part == ".githooks" ||
part == "readme" ||
part.has_prefix("readme.") ||
part == "license" ||
part.has_prefix("license.") {
return true
}
}
false
}
///|
/// Sorted eligible mbt paths. Empty includes means **/*.mbt. Ignore/protect
/// rules match files or ancestors and override includes. Documentation, script
/// directories and Git metadata remain protected. This only selects mutation
/// targets; it never removes ignored or protected support files from a snapshot.
/// Invalid rules fail even when there are no files. At most 128 rules total.
pub fn select(
snapshot : Snapshot,
includes : Array[String],
protects : Array[String],
ignores : Array[String],
) -> Result[Array[String], String] {
if includes.length() + protects.length() + ignores.length() > 128 {
return Err("MR_GLOB_LIMIT: too many selection rules")
}
for rules in [includes, protects, ignores] {
for rule in rules {
match @glob.matches(rule, "validation.mbt") {
Err(e) => return Err(e)
Ok(_) => ()
}
}
}
let includes = if includes.is_empty() { ["**/*.mbt"] } else { includes }
let selected : Array[String] = []
for entry in snapshot.entries {
let path = entry.0
if path.has_suffix(".mbt") &&
!protected_by_default(path) &&
any_rule(includes, path, false) &&
!any_rule(protects, path, true) &&
!any_rule(ignores, path, true) {
selected.push(path)
}
}
Ok(selected)
}
///|
/// Select manifest metadata for package deletion or explicitly enabled edits.
/// Root metadata is eligible only when include_root is true. Includes and all
/// default/user protection rules apply before any path becomes mutable.
pub fn select_manifests(
snapshot : Snapshot,
includes : Array[String],
protects : Array[String],
ignores : Array[String],
include_root~ : Bool,
) -> Result[Array[String], String] {
match select(snapshot, includes, protects, ignores) {
Err(e) => return Err(e)
Ok(_) => ()
}
let result : Array[String] = []
for entry in snapshot.entries {
let path = entry.0
let supported = path.has_suffix("/moon.pkg") ||
(include_root && (path == "moon.pkg" || path == "moon.mod"))
if supported &&
!protected_by_default(path) &&
(includes.is_empty() || any_rule(includes, path, false)) &&
!any_rule(protects, path, true) &&
!any_rule(ignores, path, true) {
result.push(path)
}
}
Ok(result)
}