1use crate::{
2 ApplicationCachePolicy, CacheKey, CacheModelError, CacheNamespace, CacheScope, CacheTopology,
3 DistributedCacheBackend, FreshnessPolicy, HttpCachePolicy, InvalidationSet,
4 RequestCoalescingMode, ResponseValidators, VariationKey,
5};
6use std::sync::Arc;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct CachePlanRequest {
10 namespace: CacheNamespace,
11 resource: String,
12 application_policy: Option<ApplicationCachePolicy>,
13 http_policy: HttpCachePolicy,
14 request_coalescing_mode: Option<RequestCoalescingMode>,
15}
16
17impl CachePlanRequest {
18 pub fn new(
19 namespace: CacheNamespace,
20 resource: impl Into<String>,
21 http_policy: HttpCachePolicy,
22 ) -> Result<Self, CacheModelError> {
23 Ok(Self {
24 namespace,
25 resource: crate::types::require_non_empty("cache_resource", resource.into())?,
26 application_policy: None,
27 http_policy,
28 request_coalescing_mode: None,
29 })
30 }
31
32 pub fn with_application_policy(mut self, policy: ApplicationCachePolicy) -> Self {
33 self.application_policy = Some(policy);
34 self
35 }
36
37 pub fn with_request_coalescing_mode(mut self, mode: RequestCoalescingMode) -> Self {
38 self.request_coalescing_mode = Some(mode);
39 self
40 }
41
42 pub fn request_coalescing_mode(&self) -> Option<RequestCoalescingMode> {
43 self.request_coalescing_mode
44 }
45}
46
47#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
48pub struct CacheLayerPlan {
49 pub l1: crate::LocalCacheBackend,
50 pub l2: Option<DistributedCacheBackend>,
51}
52
53#[derive(Debug, Clone, PartialEq, Eq)]
54pub struct ApplicationCachePlan {
55 key: CacheKey,
56 scope: CacheScope,
57 freshness: FreshnessPolicy,
58 tags: InvalidationSet,
59 layers: CacheLayerPlan,
60 coalescing: RequestCoalescingMode,
61}
62
63impl ApplicationCachePlan {
64 pub fn key(&self) -> &CacheKey {
65 &self.key
66 }
67
68 pub fn scope(&self) -> &CacheScope {
69 &self.scope
70 }
71
72 pub fn freshness(&self) -> FreshnessPolicy {
73 self.freshness
74 }
75
76 pub fn tags(&self) -> &InvalidationSet {
77 &self.tags
78 }
79
80 pub fn layers(&self) -> &CacheLayerPlan {
81 &self.layers
82 }
83
84 pub fn coalescing(&self) -> RequestCoalescingMode {
85 self.coalescing
86 }
87
88 pub fn shared_invalidation(&self) -> bool {
89 self.layers.l2.is_some()
90 }
91}
92
93#[derive(Debug, Clone, PartialEq, Eq)]
94pub struct HttpCachePlan {
95 scope: CacheScope,
96 variation: Option<VariationKey>,
97 validators: ResponseValidators,
98 surrogate_tags: InvalidationSet,
99 cache_control: String,
100}
101
102impl HttpCachePlan {
103 pub fn scope(&self) -> &CacheScope {
104 &self.scope
105 }
106
107 pub fn variation(&self) -> Option<&VariationKey> {
108 self.variation.as_ref()
109 }
110
111 pub fn validators(&self) -> &ResponseValidators {
112 &self.validators
113 }
114
115 pub fn surrogate_tags(&self) -> &InvalidationSet {
116 &self.surrogate_tags
117 }
118
119 pub fn cache_control(&self) -> &str {
120 &self.cache_control
121 }
122
123 pub fn edge_cacheable(&self) -> bool {
124 self.scope.is_edge_cacheable() && self.scope.is_cacheable()
125 }
126}
127
128#[derive(Debug, Clone, PartialEq, Eq)]
129pub struct CachePlan {
130 application: Option<ApplicationCachePlan>,
131 http: HttpCachePlan,
132}
133
134impl CachePlan {
135 pub fn application(&self) -> Option<&ApplicationCachePlan> {
136 self.application.as_ref()
137 }
138
139 pub fn http(&self) -> &HttpCachePlan {
140 &self.http
141 }
142}
143
144#[derive(Debug, Clone, Copy)]
145pub struct CachePlanner {
146 topology: CacheTopology,
147}
148
149impl CachePlanner {
150 pub fn new(topology: CacheTopology) -> Self {
151 Self { topology }
152 }
153
154 pub fn topology(&self) -> CacheTopology {
155 self.topology
156 }
157
158 pub fn runtime(&self) -> crate::CacheRuntime {
159 crate::CacheRuntime::new(self.topology)
160 }
161
162 #[cfg(test)]
163 #[doc(hidden)]
164 pub fn local_for_testing(&self) -> crate::CacheRuntime {
165 crate::CacheRuntime::local_for_testing(self.topology)
166 }
167
168 #[cfg(test)]
169 #[doc(hidden)]
170 pub fn local_runtime(&self) -> crate::CacheRuntime {
171 self.local_for_testing()
172 }
173
174 pub fn runtime_with_shared_runtime(
175 &self,
176 shared_runtime: Arc<dyn crate::DistributedCacheRuntime>,
177 ) -> crate::CacheRuntime {
178 crate::CacheRuntime::with_shared_runtime(self.topology, shared_runtime)
179 }
180
181 #[allow(dead_code)]
182 #[doc(hidden)]
183 #[cfg(test)]
184 #[deprecated(note = "use runtime_with_shared_runtime(shared_runtime)")]
185 pub fn shared_runtime(&self) -> crate::CacheRuntime {
186 self.runtime()
187 }
188
189 pub fn plan(&self, request: CachePlanRequest) -> Result<CachePlan, CacheModelError> {
190 let CachePlanRequest {
191 namespace,
192 resource,
193 application_policy,
194 http_policy,
195 request_coalescing_mode,
196 } = request;
197
198 let application = application_policy
199 .map(|policy| {
200 let variation = policy.scope().cache_partition_key();
201 let key = CacheKey::new(namespace.clone(), resource.clone(), variation)?;
202 let coalescing =
203 request_coalescing_mode.unwrap_or(self.topology.request_coalescing_mode());
204
205 Ok(ApplicationCachePlan {
206 key,
207 scope: policy.scope().clone(),
208 freshness: policy.freshness(),
209 tags: policy.tags().clone(),
210 layers: CacheLayerPlan {
211 l1: self.topology.l1(),
212 l2: self.topology.l2(),
213 },
214 coalescing,
215 })
216 })
217 .transpose()?;
218
219 let http = HttpCachePlan {
220 variation: http_policy.scope().variation_key(),
221 scope: http_policy.scope().clone(),
222 validators: http_policy.validators().clone(),
223 surrogate_tags: http_policy.surrogate_tags().clone(),
224 cache_control: http_policy.cache_control_value(),
225 };
226
227 Ok(CachePlan { application, http })
228 }
229}
230
231impl PartialEq for CachePlanner {
232 fn eq(&self, other: &Self) -> bool {
233 self.topology == other.topology
234 }
235}
236
237impl Eq for CachePlanner {}