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}