///|
pub struct AdmissionRule {
provider : String
max_events_per_window : Int
window_seconds : Int
} derive(Eq, Debug)
///|
pub fn AdmissionRule::new(
provider : String,
max_events_per_window : Int,
window_seconds : Int,
) -> AdmissionRule {
{ provider, max_events_per_window, window_seconds }
}
///|
pub struct AdmissionCounter {
provider : String
window_start : Int
count : Int
} derive(Eq, Debug)
///|
pub fn AdmissionCounter::new(
provider : String,
window_start : Int,
count : Int,
) -> AdmissionCounter {
{ provider, window_start, count }
}
///|
pub struct AdmissionState {
counters : Array[AdmissionCounter]
} derive(Eq, Debug)
///|
pub fn AdmissionState::empty() -> AdmissionState {
{ counters: [] }
}
///|
fn find_counter_index(state : AdmissionState, provider : String) -> Int {
for i in 0.. AdmissionDecision {
{ allowed, reason, state }
}
///|
pub fn check_admission(
provider : String,
now : Int,
rule : AdmissionRule,
state : AdmissionState,
) -> AdmissionDecision {
if provider != rule.provider {
AdmissionDecision::new(true, "rule does not apply", state)
} else {
let index = find_counter_index(state, provider)
if index < 0 {
let counters = Array::makei(state.counters.length() + 1, i => {
if i < state.counters.length() {
state.counters[i]
} else {
AdmissionCounter::new(provider, now, 1)
}
})
AdmissionDecision::new(true, "new window", { counters, })
} else {
let counter = state.counters[index]
if now - counter.window_start > rule.window_seconds {
let counters = Array::makei(state.counters.length(), i => {
if i == index {
AdmissionCounter::new(provider, now, 1)
} else {
state.counters[i]
}
})
AdmissionDecision::new(true, "window reset", { counters, })
} else if counter.count >= rule.max_events_per_window {
AdmissionDecision::new(false, "admission window full", state)
} else {
let counters = Array::makei(state.counters.length(), i => {
if i == index {
AdmissionCounter::new(
provider,
counter.window_start,
counter.count + 1,
)
} else {
state.counters[i]
}
})
AdmissionDecision::new(true, "counted", { counters, })
}
}
}
}
///|
pub fn default_admission_rules() -> Array[AdmissionRule] {
[
AdmissionRule::new("github", 100, 60),
AdmissionRule::new("feishu", 120, 60),
AdmissionRule::new("stripe", 80, 60),
AdmissionRule::new("generic", 60, 60),
]
}
///|
pub fn admission_rule_for(provider : String) -> AdmissionRule {
let rules = default_admission_rules()
for i in 0..