use std::time::Duration;
use super::invalidation::EntityTag;
#[derive(Debug, Clone)]
pub struct CacheOptions {
pub ttl: Option<Duration>,
pub policy: CachePolicy,
pub write_policy: WritePolicy,
pub tags: Vec<EntityTag>,
pub max_size: Option<usize>,
pub cache_empty: bool,
pub bypass: bool,
pub stale_while_revalidate: Option<Duration>,
}
impl Default for CacheOptions {
fn default() -> Self {
Self {
ttl: Some(Duration::from_secs(300)), policy: CachePolicy::CacheFirst,
write_policy: WritePolicy::WriteThrough,
tags: Vec::new(),
max_size: Some(1024 * 1024), cache_empty: true,
bypass: false,
stale_while_revalidate: None,
}
}
}
impl CacheOptions {
pub fn ttl(duration: Duration) -> Self {
Self {
ttl: Some(duration),
..Default::default()
}
}
pub fn no_expire() -> Self {
Self {
ttl: None,
..Default::default()
}
}
pub fn bypass() -> Self {
Self {
bypass: true,
..Default::default()
}
}
pub fn with_ttl(mut self, duration: Duration) -> Self {
self.ttl = Some(duration);
self
}
pub fn with_policy(mut self, policy: CachePolicy) -> Self {
self.policy = policy;
self
}
pub fn with_write_policy(mut self, policy: WritePolicy) -> Self {
self.write_policy = policy;
self
}
pub fn with_tag(mut self, tag: impl Into<EntityTag>) -> Self {
self.tags.push(tag.into());
self
}
pub fn with_tags(mut self, tags: impl IntoIterator<Item = EntityTag>) -> Self {
self.tags.extend(tags);
self
}
pub fn with_max_size(mut self, size: usize) -> Self {
self.max_size = Some(size);
self
}
pub fn no_size_limit(mut self) -> Self {
self.max_size = None;
self
}
pub fn no_cache_empty(mut self) -> Self {
self.cache_empty = false;
self
}
pub fn stale_while_revalidate(mut self, duration: Duration) -> Self {
self.stale_while_revalidate = Some(duration);
self
}
pub fn short() -> Self {
Self::ttl(Duration::from_secs(60))
}
pub fn medium() -> Self {
Self::ttl(Duration::from_secs(300))
}
pub fn long() -> Self {
Self::ttl(Duration::from_secs(3600))
}
pub fn daily() -> Self {
Self::ttl(Duration::from_secs(86400))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum CachePolicy {
#[default]
CacheFirst,
NetworkFirst,
CacheOnly,
NetworkOnly,
StaleWhileRevalidate,
}
impl CachePolicy {
pub fn should_check_cache(&self) -> bool {
matches!(
self,
Self::CacheFirst | Self::CacheOnly | Self::StaleWhileRevalidate
)
}
pub fn should_fetch(&self) -> bool {
matches!(
self,
Self::CacheFirst | Self::NetworkFirst | Self::NetworkOnly | Self::StaleWhileRevalidate
)
}
pub fn should_update_cache(&self) -> bool {
matches!(
self,
Self::CacheFirst | Self::NetworkFirst | Self::StaleWhileRevalidate
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum WritePolicy {
#[default]
WriteThrough,
WriteBack,
WriteDelayed,
}
pub mod presets {
use super::*;
pub fn volatile() -> CacheOptions {
CacheOptions::ttl(Duration::from_secs(30))
.with_policy(CachePolicy::StaleWhileRevalidate)
.stale_while_revalidate(Duration::from_secs(60))
}
pub fn user_data() -> CacheOptions {
CacheOptions::ttl(Duration::from_secs(300)).with_policy(CachePolicy::CacheFirst)
}
pub fn reference_data() -> CacheOptions {
CacheOptions::ttl(Duration::from_secs(3600)).with_policy(CachePolicy::CacheFirst)
}
pub fn static_data() -> CacheOptions {
CacheOptions::ttl(Duration::from_secs(86400)).with_policy(CachePolicy::CacheFirst)
}
pub fn session() -> CacheOptions {
CacheOptions::ttl(Duration::from_secs(1800)) .with_policy(CachePolicy::CacheFirst)
}
pub fn realtime() -> CacheOptions {
CacheOptions::bypass()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_options() {
let opts = CacheOptions::default();
assert_eq!(opts.ttl, Some(Duration::from_secs(300)));
assert_eq!(opts.policy, CachePolicy::CacheFirst);
assert!(opts.cache_empty);
assert!(!opts.bypass);
}
#[test]
fn test_options_builder() {
let opts = CacheOptions::ttl(Duration::from_secs(60))
.with_policy(CachePolicy::NetworkFirst)
.with_tag(EntityTag::new("User"))
.no_cache_empty();
assert_eq!(opts.ttl, Some(Duration::from_secs(60)));
assert_eq!(opts.policy, CachePolicy::NetworkFirst);
assert_eq!(opts.tags.len(), 1);
assert!(!opts.cache_empty);
}
#[test]
fn test_cache_policy() {
assert!(CachePolicy::CacheFirst.should_check_cache());
assert!(CachePolicy::CacheFirst.should_fetch());
assert!(CachePolicy::CacheFirst.should_update_cache());
assert!(!CachePolicy::NetworkOnly.should_check_cache());
assert!(CachePolicy::NetworkOnly.should_fetch());
assert!(!CachePolicy::NetworkOnly.should_update_cache());
assert!(CachePolicy::CacheOnly.should_check_cache());
assert!(!CachePolicy::CacheOnly.should_fetch());
assert!(!CachePolicy::CacheOnly.should_update_cache());
}
#[test]
fn test_presets() {
let volatile = presets::volatile();
assert_eq!(volatile.ttl, Some(Duration::from_secs(30)));
let reference = presets::reference_data();
assert_eq!(reference.ttl, Some(Duration::from_secs(3600)));
let realtime = presets::realtime();
assert!(realtime.bypass);
}
}