Skip to main content

coil_cache/
policy.rs

1use crate::{CacheModelError, CacheScope, FreshnessPolicy, InvalidationSet, ResponseValidators};
2
3#[derive(Debug, Clone, PartialEq, Eq)]
4pub struct ApplicationCachePolicy {
5    scope: CacheScope,
6    freshness: FreshnessPolicy,
7    tags: InvalidationSet,
8}
9
10impl ApplicationCachePolicy {
11    pub fn new(
12        scope: CacheScope,
13        freshness: FreshnessPolicy,
14        tags: InvalidationSet,
15    ) -> Result<Self, CacheModelError> {
16        if !scope.is_cacheable() {
17            return Err(CacheModelError::UncacheableApplicationScope);
18        }
19
20        Ok(Self {
21            scope,
22            freshness,
23            tags,
24        })
25    }
26
27    pub fn scope(&self) -> &CacheScope {
28        &self.scope
29    }
30
31    pub fn freshness(&self) -> FreshnessPolicy {
32        self.freshness
33    }
34
35    pub fn tags(&self) -> &InvalidationSet {
36        &self.tags
37    }
38}
39
40#[derive(Debug, Clone, PartialEq, Eq)]
41pub struct HttpCachePolicy {
42    scope: CacheScope,
43    freshness: Option<FreshnessPolicy>,
44    validators: ResponseValidators,
45    surrogate_tags: InvalidationSet,
46}
47
48impl HttpCachePolicy {
49    pub fn new(
50        scope: CacheScope,
51        freshness: Option<FreshnessPolicy>,
52        validators: ResponseValidators,
53        surrogate_tags: InvalidationSet,
54    ) -> Result<Self, CacheModelError> {
55        match (scope.is_cacheable(), freshness) {
56            (true, None) => Err(CacheModelError::MissingHttpFreshness),
57            (false, Some(_)) => Err(CacheModelError::NoStoreCannotDefineFreshness),
58            _ => Ok(Self {
59                scope,
60                freshness,
61                validators,
62                surrogate_tags,
63            }),
64        }
65    }
66
67    pub fn scope(&self) -> &CacheScope {
68        &self.scope
69    }
70
71    pub fn freshness(&self) -> Option<FreshnessPolicy> {
72        self.freshness
73    }
74
75    pub fn validators(&self) -> &ResponseValidators {
76        &self.validators
77    }
78
79    pub fn surrogate_tags(&self) -> &InvalidationSet {
80        &self.surrogate_tags
81    }
82
83    pub fn cache_control_value(&self) -> String {
84        match (self.scope.visibility(), self.freshness) {
85            (crate::CacheVisibility::NoStore, _) => "no-store".to_string(),
86            (crate::CacheVisibility::Public, Some(freshness)) => {
87                cache_control_value("public", freshness)
88            }
89            (crate::CacheVisibility::Private, Some(freshness)) => {
90                cache_control_value("private", freshness)
91            }
92            (_, None) => "no-store".to_string(),
93        }
94    }
95}
96
97fn cache_control_value(visibility: &str, freshness: FreshnessPolicy) -> String {
98    let mut directives = vec![format!("{visibility}, max-age={}", freshness.ttl_seconds())];
99
100    if let Some(swr) = freshness.stale_while_revalidate_seconds() {
101        directives.push(format!("stale-while-revalidate={swr}"));
102    }
103
104    directives.join(", ")
105}