///|
pub(all) enum CacheActionKind {
ServeFresh
Revalidate
Fetch
ServeStale
Bypass
OnlyIfCachedMiss
} derive(Eq, Compare, Debug)
///|
pub fn CacheActionKind::label(self : CacheActionKind) -> String {
match self {
ServeFresh => "SERVE_FRESH"
Revalidate => "REVALIDATE"
Fetch => "FETCH"
ServeStale => "SERVE_STALE"
Bypass => "BYPASS"
OnlyIfCachedMiss => "ONLY_IF_CACHED_MISS"
}
}
///|
pub(all) struct CacheTrace {
mut action : CacheActionKind
reasons : Array[CacheReason]
mut age : AgeCalculation?
mut freshness_lifetime : DeltaSeconds?
mut stale_by : DeltaSeconds?
mut primary_key : String?
mut selected_variant : String?
mut validator : String?
generated_headers : HeaderMap
} derive(Eq, Debug)
///|
pub fn CacheTrace::new(action : CacheActionKind) -> CacheTrace {
CacheTrace::{
action,
reasons: [],
age: None,
freshness_lifetime: None,
stale_by: None,
primary_key: None,
selected_variant: None,
validator: None,
generated_headers: HeaderMap::new(),
}
}
///|
pub fn CacheTrace::add_reason(self : CacheTrace, reason : CacheReason) -> Unit {
self.reasons.push(reason)
}
///|
pub fn CacheTrace::has_reason(
self : CacheTrace,
code : CacheReasonCode,
) -> Bool {
self.reasons.any(fn(reason) { reason.code == code })
}
///|
fn delta_to_json(delta : DeltaSeconds) -> Json {
Json::number(delta.seconds().to_double(), repr=delta.seconds().to_string())
}
///|
fn reason_to_json(reason : CacheReason) -> Json {
let object : Map[String, Json] = {
"code": reason.code.code(),
"message": reason.code.message(),
}
match reason.detail {
Some(detail) => object["detail"] = Json::string(detail)
None => ()
}
match reason.rfc {
Some(rfc) => object["rfc"] = Json::string(rfc)
None => ()
}
Json::object(object)
}
///|
pub fn CacheTrace::to_json(self : CacheTrace) -> Json {
let object : Map[String, Json] = {
"action": self.action.label(),
"reasons": Json::array(self.reasons.map(reason_to_json)),
}
match self.age {
Some(age) => {
object["apparent_age"] = delta_to_json(age.apparent_age)
object["response_delay"] = delta_to_json(age.response_delay)
object["corrected_initial_age"] = delta_to_json(age.corrected_initial_age)
object["resident_time"] = delta_to_json(age.resident_time)
object["current_age"] = delta_to_json(age.current_age)
object["clock_clamped"] = Json::boolean(age.clock_clamped)
object["overflow_clamped"] = Json::boolean(age.overflow_clamped)
}
None => ()
}
match self.freshness_lifetime {
Some(lifetime) => object["freshness_lifetime"] = delta_to_json(lifetime)
None => ()
}
match self.stale_by {
Some(stale) => object["stale_by"] = delta_to_json(stale)
None => ()
}
match self.primary_key {
Some(key) => object["primary_key"] = Json::string(key)
None => ()
}
match self.selected_variant {
Some(variant) => object["selected_variant"] = Json::string(variant)
None => ()
}
match self.validator {
Some(validator) => object["validator"] = Json::string(validator)
None => ()
}
if !self.generated_headers.is_empty() {
let headers : Map[String, Json] = Map([])
for pair in self.generated_headers.redacted().pairs() {
headers[pair.0] = Json::string(pair.1)
}
object["generated_headers"] = Json::object(headers)
}
Json::object(object)
}
///|
pub fn CacheTrace::json_report(self : CacheTrace) -> String {
self.to_json().stringify(indent=2)
}
///|
pub fn CacheTrace::text_report(self : CacheTrace) -> String {
let lines : Array[String] = ["Decision: \{self.action.label()}"]
match self.primary_key {
Some(key) => lines.push("Primary key: \{key}")
None => ()
}
match self.selected_variant {
Some(variant) => lines.push("Variant: \{variant}")
None => ()
}
match self.age {
Some(age) => {
lines.push("Apparent age: \{age.apparent_age.seconds()}s")
lines.push("Response delay: \{age.response_delay.seconds()}s")
lines.push(
"Corrected initial age: \{age.corrected_initial_age.seconds()}s",
)
lines.push("Resident time: \{age.resident_time.seconds()}s")
lines.push("Current age: \{age.current_age.seconds()}s")
}
None => ()
}
match self.freshness_lifetime {
Some(lifetime) => lines.push("Freshness lifetime: \{lifetime.seconds()}s")
None => ()
}
match self.stale_by {
Some(stale) => lines.push("Stale by: \{stale.seconds()}s")
None => ()
}
match self.validator {
Some(validator) => lines.push("Validator: \{validator}")
None => ()
}
for pair in self.generated_headers.redacted().pairs() {
lines.push("Generated header: \{pair.0}: \{pair.1}")
}
for reason in self.reasons {
let suffix = match reason.rfc {
Some(rfc) => " [\{rfc}]"
None => ""
}
lines.push(
"Reason \{reason.code.code()}: \{reason.code.message()}\{suffix}",
)
}
lines.join("\n")
}