///|
fn available_budget(
state : GrantState,
remaining_calls : Int?,
remaining_bytes : Int64?,
) -> Budget {
{
max_calls: remaining_calls,
max_bytes: remaining_bytes,
expires_at: state.grant.budget.expires_at,
}
}
///|
fn reserve_calls(available : Int?, requested : Int?) -> Int? {
match (available, requested) {
(Some(available), Some(requested)) => Some(available - requested)
_ => available
}
}
///|
fn reserve_bytes(available : Int64?, requested : Int64?) -> Int64? {
match (available, requested) {
(Some(available), Some(requested)) => Some(available - requested)
_ => available
}
}
///|
fn grant_is_active(state : GrantState, now : Int64) -> Bool {
match state.grant.budget.expires_at {
Some(expiry) => now < expiry
None => true
}
}
///|
/// Prefer the tighter of two usable parents so broader authority remains
/// available for child requests that genuinely require it.
fn candidate_is_tighter(candidate : GrantState, current : GrantState) -> Bool {
effect_contains(current.grant.effect, candidate.grant.effect)
is Containment::Contained &&
!(effect_contains(candidate.grant.effect, current.grant.effect)
is Containment::Contained)
}
///|
pub fn Runtime::delegate(
self : Runtime,
child_id : String,
requests : Array[EffectRequest],
now : Int64,
) -> Permit raise PermitError {
if now < 0L {
raise PermitError::InvalidLogicalTime(now)
}
let child = compile_plan(child_id, requests)
let calls = self.states.map(state => state.remaining_calls)
let bytes = self.states.map(state => state.remaining_bytes)
for child_grant in child.grants {
let mut candidate : Int? = None
for index, state in self.states {
let scope_matches = effect_contains(
state.grant.effect,
child_grant.effect,
)
is Containment::Contained
let budget_matches = budget_contains(
available_budget(state, calls[index], bytes[index]),
child_grant.budget,
)
if grant_is_active(state, now) && scope_matches && budget_matches {
match candidate {
None => candidate = Some(index)
Some(current) =>
if candidate_is_tighter(state, self.states[current]) {
candidate = Some(index)
}
}
if state.grant.effect == child_grant.effect {
break
}
}
}
match candidate {
None =>
raise PermitError::ChildAuthorityExceeded(
child_grant.effect.canonical(),
)
Some(index) => {
calls[index] = reserve_calls(calls[index], child_grant.budget.max_calls)
bytes[index] = reserve_bytes(bytes[index], child_grant.budget.max_bytes)
}
}
}
for index, state in self.states {
state.remaining_calls = calls[index]
state.remaining_bytes = bytes[index]
}
child
}