pub const MAX_DISPLAY_AGE_SECONDS: i64 = 600;
pub const FUTURE_CLOCK_TOLERANCE_SECONDS: i64 = 5;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum UsageCacheEligibility {
Eligible,
MissingOrInvalidTimestamp,
Expired,
FutureTimestampBeyondTolerance,
}
impl UsageCacheEligibility {
pub const fn is_display_eligible(self) -> bool {
matches!(self, Self::Eligible)
}
}
pub const fn classify_display_age_seconds(age_seconds: Option<i64>) -> UsageCacheEligibility {
let Some(age_seconds) = age_seconds else {
return UsageCacheEligibility::MissingOrInvalidTimestamp;
};
if age_seconds < -FUTURE_CLOCK_TOLERANCE_SECONDS {
UsageCacheEligibility::FutureTimestampBeyondTolerance
} else if age_seconds >= MAX_DISPLAY_AGE_SECONDS {
UsageCacheEligibility::Expired
} else {
UsageCacheEligibility::Eligible
}
}
#[cfg(test)]
mod tests {
use super::{UsageCacheEligibility, classify_display_age_seconds};
use pretty_assertions::assert_eq;
#[test]
fn classify_display_age_covers_contract_boundaries() {
let cases = [
(
"missing or invalid timestamp",
None,
UsageCacheEligibility::MissingOrInvalidTimestamp,
),
(
"future by six seconds",
Some(-6),
UsageCacheEligibility::FutureTimestampBeyondTolerance,
),
(
"largest future offset",
Some(i64::MIN),
UsageCacheEligibility::FutureTimestampBeyondTolerance,
),
(
"future by five seconds",
Some(-5),
UsageCacheEligibility::Eligible,
),
("current", Some(0), UsageCacheEligibility::Eligible),
(
"past by 599 seconds",
Some(599),
UsageCacheEligibility::Eligible,
),
(
"past by 600 seconds",
Some(600),
UsageCacheEligibility::Expired,
),
(
"largest past offset",
Some(i64::MAX),
UsageCacheEligibility::Expired,
),
];
for (label, age_seconds, expected) in cases {
let actual = classify_display_age_seconds(age_seconds);
assert_eq!(actual, expected, "{label}");
assert_eq!(
actual.is_display_eligible(),
expected == UsageCacheEligibility::Eligible,
"{label} display eligibility"
);
}
}
}