///|
fn built_in_binary_macro() -> MacroDefinition {
{
name: "binary",
assignments: [
{ name: "diff", state: Unset, },
{ name: "merge", state: Unset, },
{ name: "text", state: Unset, },
],
source: "",
line: 0,
}
}
///|
fn find_macro(
macros : Array[MacroDefinition],
name : String,
) -> MacroDefinition? {
for index = macros.length() - 1; index >= 0; index = index - 1 {
if macros[index].name == name {
return Some(macros[index])
}
}
None
}
///|
fn resolve_macro(
macros : Array[MacroDefinition],
assignment : Assignment,
) -> Array[Assignment]? {
guard assignment.state is Set else { return None }
if assignment.name == "binary" {
Some(built_in_binary_macro().assignments)
} else {
match find_macro(macros, assignment.name) {
Some(definition) => Some(definition.assignments)
None => None
}
}
}
///|
fn apply_assignment(
assignment : Assignment,
rule : Rule,
rule_index : Int,
macros : Array[MacroDefinition],
resolved : Map[String, ResolvedAttribute],
expansion_stack : Array[String],
) -> Unit {
resolved[assignment.name] = {
name: assignment.name,
state: assignment.state,
source: rule.source,
line: rule.line,
pattern: rule.pattern,
rule_index,
}
guard resolve_macro(macros, assignment) is Some(expansion) else { return }
if expansion_stack.contains(assignment.name) {
return
}
let next_stack = expansion_stack.copy()
next_stack.push(assignment.name)
for nested in expansion {
apply_assignment(nested, rule, rule_index, macros, resolved, next_stack)
}
}
///|
/// Evaluates all matching rules for a repository-relative path.
pub fn RuleSet::evaluate(self : RuleSet, path : String) -> Evaluation {
let resolved : Map[String, ResolvedAttribute] = Map([])
let matched_rules : Array[Int] = []
for index, rule in self.rules {
if match_pattern(rule.pattern, path, base_dir=rule.base_dir) {
matched_rules.push(index)
for assignment in rule.assignments {
apply_assignment(assignment, rule, index, self.macros, resolved, [])
}
}
}
{ path, attributes: resolved.values().collect(), matched_rules, }
}
///|
/// Evaluates a batch of paths while preserving input order.
pub fn RuleSet::evaluate_many(
self : RuleSet,
paths : ArrayView[String],
) -> Array[Evaluation] {
paths.map(path => self.evaluate(path))
}
///|
/// Returns the resolved state of `name`, or `Unspecified` when no rule decided it.
pub fn Evaluation::attribute(
self : Evaluation,
name : String,
) -> AttributeState {
for attribute in self.attributes {
if attribute.name == name {
return attribute.state
}
}
Unspecified
}