///|
fn find_covering_grant(approved : Permit, requested : Grant) -> Grant? {
let mut candidate : Grant? = None
for grant in approved.grants {
if grant_contains(grant, requested) {
match candidate {
None => candidate = Some(grant)
Some(current) => {
let grant_is_tighter = effect_contains(current.effect, grant.effect)
is Containment::Contained &&
!(effect_contains(grant.effect, current.effect)
is Containment::Contained)
if grant_is_tighter {
candidate = Some(grant)
}
}
}
}
}
candidate
}
///|
pub fn diff_permits(approved : Permit, requested : Permit) -> PermitDiff {
let entries : Array[DiffEntry] = []
for grant in requested.grants {
match find_covering_grant(approved, grant) {
Some(parent) =>
entries.push({
effect: grant.effect.canonical(),
budget: grant.budget.canonical(),
verdict: Verdict::Allow,
covered_by: Some(parent.id),
explanation: "already covered by " + parent.id,
})
None =>
entries.push({
effect: grant.effect.canonical(),
budget: grant.budget.canonical(),
verdict: Verdict::NeedsApproval,
covered_by: None,
explanation: "scope or budget exceeds the approved permit",
})
}
}
{ approved_id: approved.id, requested_id: requested.id, entries, }
}
///|
pub fn PermitDiff::entries(self : PermitDiff) -> Array[DiffEntry] {
self.entries.copy()
}
///|
pub fn PermitDiff::requires_approval(self : PermitDiff) -> Bool {
self.entries.any(entry => entry.verdict is Verdict::NeedsApproval)
}
///|
pub fn PermitDiff::render(self : PermitDiff) -> String {
let lines : Array[String] = [
"diff:" + self.approved_id + "->" + self.requested_id,
]
for entry in self.entries {
let status = match entry.verdict {
Allow => "COVERED"
Deny => "DENY"
NeedsApproval => "NEEDS_APPROVAL"
}
let coverage = match entry.covered_by {
Some(grant_id) => " via " + grant_id
None => ""
}
lines.push(
status +
" " +
entry.effect +
" | " +
entry.budget +
coverage +
" | " +
entry.explanation,
)
}
lines.join("\n")
}