///|
pub(all) enum Severity {
Info
Warning
Error
} derive(Eq, Debug)
///|
pub(all) struct ValidationIssue {
severity : Severity
field : String
message : String
} derive(Eq, Debug)
///|
pub fn ValidationIssue::error(
field : String,
message : String,
) -> ValidationIssue {
ValidationIssue::{ severity: Error, field, message }
}
///|
pub fn ValidationIssue::warning(
field : String,
message : String,
) -> ValidationIssue {
ValidationIssue::{ severity: Warning, field, message }
}
///|
pub fn ValidationIssue::info(
field : String,
message : String,
) -> ValidationIssue {
ValidationIssue::{ severity: Info, field, message }
}
///|
pub fn ValidationIssue::is_error(self : ValidationIssue) -> Bool {
self.severity == Error
}
///|
pub fn ValidationIssue::field(self : ValidationIssue) -> String {
self.field
}
///|
pub fn ValidationIssue::message(self : ValidationIssue) -> String {
self.message
}
///|
pub(all) struct ValidationReport {
mut issues : Array[ValidationIssue]
} derive(Debug)
///|
pub fn ValidationReport::new() -> ValidationReport {
ValidationReport::{ issues: [] }
}
///|
pub fn ValidationReport::push(
self : ValidationReport,
issue : ValidationIssue,
) -> Unit {
self.issues.push(issue)
}
///|
pub fn ValidationReport::len(self : ValidationReport) -> Int {
self.issues.length()
}
///|
pub fn ValidationReport::is_ok(self : ValidationReport) -> Bool {
for issue in self.issues {
if issue.is_error() {
return false
}
}
true
}
///|
pub fn ValidationReport::error_count(self : ValidationReport) -> Int {
let mut count = 0
for issue in self.issues {
if issue.is_error() {
count = count + 1
}
}
count
}
///|
pub fn ValidationReport::warning_count(self : ValidationReport) -> Int {
let mut count = 0
for issue in self.issues {
if issue.severity == Warning {
count = count + 1
}
}
count
}
///|
pub fn ValidationReport::summary(self : ValidationReport) -> String {
"errors=\{self.error_count()} warnings=\{self.warning_count()} issues=\{self.len()}"
}
///|
pub fn LimitSpec::validate(self : LimitSpec) -> ValidationReport {
let report = ValidationReport::new()
if self.spec_name.length() == 0 {
report.push(ValidationIssue::error("name", "name must not be empty"))
}
if self.first <= 0 {
report.push(ValidationIssue::error("limit", "limit must be positive"))
}
if self.second <= 0 {
report.push(ValidationIssue::error("period", "period must be positive"))
}
if self.kind == TokenBucket && self.third <= 0 {
report.push(
ValidationIssue::error(
"refill_period_ms", "refill period must be positive",
),
)
}
if self.kind == TokenBucket && self.first < self.second {
report.push(
ValidationIssue::warning(
"capacity", "capacity is smaller than refill quantum",
),
)
}
report
}
///|
pub fn LimitSpec::kind_name(self : LimitSpec) -> String {
match self.kind {
TokenBucket => "token_bucket"
FixedWindow => "fixed_window"
SlidingLog => "sliding_log"
GcraSpec => "gcra"
}
}
///|
pub(all) struct RuleSet {
mut specs : Array[LimitSpec]
} derive(Debug)
///|
pub fn RuleSet::new() -> RuleSet {
RuleSet::{ specs: [] }
}
///|
pub fn RuleSet::push(self : RuleSet, spec : LimitSpec) -> Unit {
self.specs.push(spec)
}
///|
pub fn RuleSet::len(self : RuleSet) -> Int {
self.specs.length()
}
///|
pub fn RuleSet::get(self : RuleSet, index : Int) -> LimitSpec? {
self.specs.get(index)
}
///|
pub fn RuleSet::validate(self : RuleSet) -> ValidationReport {
let report = ValidationReport::new()
if self.specs.length() == 0 {
report.push(ValidationIssue::warning("rules", "rule set is empty"))
}
for spec in self.specs {
let nested = spec.validate()
for issue in nested.issues {
report.push(issue)
}
}
report
}
///|
pub fn RuleSet::parse_lines(text : String) -> RuleSet {
let rules = RuleSet::new()
let lines = split_lines(text)
for line in lines {
let trimmed = line.trim().to_owned()
if trimmed.length() > 0 && !starts_with(trimmed, "#") {
rules.push(LimitSpec::parse(trimmed))
}
}
rules
}
///|
fn starts_with(text : String, prefix : String) -> Bool {
if prefix.length() > text.length() {
false
} else {
text[0:prefix.length()] == prefix
}
}
///|
fn split_lines(text : String) -> Array[String] {
let lines : Array[String] = []
let mut start = 0
for i in 0..