///|
fn lower(text : String) -> String {
text.to_lower()
}
///|
fn has_text(text : String) -> Bool {
!text.trim().is_empty()
}
///|
fn contains_ci(text : String, needle : String) -> Bool {
lower(text).contains(lower(needle))
}
///|
fn json_escape(text : String) -> String {
text
.replace_all(old="\\", new="\\\\")
.replace_all(old="\"", new="\\\"")
.replace_all(old="\n", new="\\n")
}
///|
fn clamp(value : Int, low : Int, high : Int) -> Int {
if value < low {
low
} else if value > high {
high
} else {
value
}
}
///|
fn severity_text(severity : Severity) -> String {
match severity {
Pass => "pass"
Warn => "warn"
Fail => "fail"
}
}
///|
fn region_text(region : Region) -> String {
match region {
NA => "na"
EU => "eu"
APAC => "apac"
CN => "cn"
LATAM => "latam"
MEA => "mea"
OtherRegion => "other"
}
}
///|
fn tier_text(tier : UserTier) -> String {
match tier {
Free => "free"
Pro => "pro"
Enterprise => "enterprise"
Staff => "staff"
Internal => "internal"
}
}
///|
pub fn mode_text(mode : RolloutMode) -> String {
match mode {
Shadow => "shadow"
DryRun => "dry-run"
Live => "live"
KillSwitch => "kill-switch"
}
}
///|
fn decision_text(decision : GateDecision) -> String {
match decision {
Advance => "advance"
Hold => "hold"
Rollback => "rollback"
Abort => "abort"
}
}
///|
fn kind_text(kind : ValidationKind) -> String {
match kind {
PlanShape => "plan"
SegmentShape => "segment"
RiskBudgetShape => "budget"
MetricGate => "metrics"
BlastRadiusGate => "blast-radius"
ScheduleGate => "schedule"
IdentityGate => "identity"
}
}
///|
fn percent_text(bp : Int) -> String {
let whole = bp / 100
let frac = bp % 100
if frac == 0 {
whole.to_string() + "%"
} else if frac < 10 {
whole.to_string() + ".0" + frac.to_string() + "%"
} else {
whole.to_string() + "." + frac.to_string() + "%"
}
}
///|
fn parse_digits(text : String) -> Int? {
let trimmed = text.trim().to_owned()
if trimmed.is_empty() {
return None
}
let mut value = 0
for ch in trimmed {
let code = ch.to_int()
if code < 48 || code > 57 {
return None
}
value = value * 10 + code - 48
}
Some(value)
}
///|
fn version_score(version : String) -> Int {
let parts = version.split(".").to_array()
let major = if parts.length() > 0 {
match parse_digits(parts[0].to_owned()) {
Some(v) => v
None => 0
}
} else {
0
}
let minor = if parts.length() > 1 {
match parse_digits(parts[1].to_owned()) {
Some(v) => v
None => 0
}
} else {
0
}
let patch = if parts.length() > 2 {
match parse_digits(parts[2].to_owned()) {
Some(v) => v
None => 0
}
} else {
0
}
major * 1000000 + minor * 1000 + patch
}