///|
/// Escapes a string for JSON output.
pub fn json_escape(value : String) -> String {
let builder = StringBuilder(size_hint=value.length())
for index = 0; index < value.length(); index = index + 1 {
match value.unsafe_get(index) {
'"' => builder.write_string("\\\"")
'\\' => builder.write_string("\\\\")
'\n' => builder.write_string("\\n")
'\r' => builder.write_string("\\r")
'\t' => builder.write_string("\\t")
unit => builder.write_char(unit.to_int().unsafe_to_char())
}
}
builder.to_string()
}
///|
/// Stable snake-case name for a decision kind.
pub fn decision_kind_name(kind : DecisionKind) -> String {
match kind {
Acquired => "acquired"
Renewed => "renewed"
Released => "released"
Transferred => "transferred"
Reaped => "reaped"
Rejected => "rejected"
}
}
///|
/// Encodes a lease as stable JSON.
pub fn Lease::to_json(self : Lease) -> String {
"{\"resource\":\"\{json_escape(self.resource)}\",\"holder\":\"\{json_escape(self.holder)}\",\"token\":\{self.token},\"revision\":\{self.revision},\"issued_at\":\{self.issued_at},\"expires_at\":\{self.expires_at}}"
}
///|
/// Encodes an audit event as stable JSON.
pub fn LeaseEvent::to_json(self : LeaseEvent) -> String {
"{\"resource\":\"\{json_escape(self.resource)}\",\"kind\":\"\{decision_kind_name(self.kind)}\",\"at\":\{self.at},\"holder\":\"\{json_escape(self.holder)}\",\"token\":\{self.token},\"revision\":\{self.revision},\"detail\":\"\{json_escape(self.detail)}\"}"
}
///|
/// Computes point-in-time table metrics.
pub fn LeaseTable::metrics(self : LeaseTable, now : Int) -> LeaseMetrics {
let mut stored = 0
let mut active = 0
let mut expired = 0
let mut highest = 0
for state in self.states {
match state.lease {
Some(lease) => {
stored = stored + 1
if lease.is_active(now) {
active = active + 1
} else {
expired = expired + 1
}
if lease.token > highest {
highest = lease.token
}
}
None => ()
}
if state.next_token - 1 > highest {
highest = state.next_token - 1
}
}
{
resources: self.states.length(),
stored_leases: stored,
active_leases: active,
expired_leases: expired,
highest_token: highest,
events: self.events.length(),
accepted_commands: self.accepted_commands,
rejected_commands: self.rejected_commands,
}
}
///|
/// Encodes metrics as stable JSON.
pub fn LeaseMetrics::to_json(self : LeaseMetrics) -> String {
"{\"resources\":\{self.resources},\"stored_leases\":\{self.stored_leases},\"active_leases\":\{self.active_leases},\"expired_leases\":\{self.expired_leases},\"highest_token\":\{self.highest_token},\"events\":\{self.events},\"accepted_commands\":\{self.accepted_commands},\"rejected_commands\":\{self.rejected_commands}}"
}
///|
/// Encodes a safety audit as stable JSON.
pub fn AuditReport::to_json(self : AuditReport) -> String {
let builder = StringBuilder()
builder.write_string(
"{\"valid\":\{self.valid},\"checked_events\":\{self.checked_events},\"findings\":[",
)
for index = 0; index < self.findings.length(); index = index + 1 {
if index > 0 {
builder.write_char(',')
}
let finding = self.findings[index]
builder.write_string(
"{\"code\":\"\{json_escape(finding.code)}\",\"resource\":\"\{json_escape(finding.resource)}\",\"event_index\":\{finding.event_index},\"message\":\"\{json_escape(finding.message)}\"}",
)
}
builder.write_string("]}")
builder.to_string()
}