http_cache/managers/
cacache.rs1use std::path::PathBuf;
2
3use crate::{CacheManager, HttpResponse, Result};
4
5use http_cache_semantics::CachePolicy;
6use serde::{Deserialize, Serialize};
7
8#[derive(Clone)]
10pub struct CACacheManager {
11 pub path: PathBuf,
13 pub remove_opts: cacache::RemoveOpts,
15}
16
17impl std::fmt::Debug for CACacheManager {
18 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19 f.debug_struct("CACacheManager").field("path", &self.path).finish()
20 }
21}
22
23#[cfg(feature = "postcard")]
25#[derive(Debug, Deserialize, Serialize)]
26struct Store {
27 response: HttpResponse,
28 policy: CachePolicy,
29}
30
31#[cfg(feature = "bincode")]
35#[derive(Debug, Deserialize, Serialize)]
36struct BincodeStore {
37 response: LegacyHttpResponse,
38 policy: CachePolicy,
39}
40
41#[cfg(feature = "bincode")]
42use crate::{HttpHeaders, HttpVersion, Url};
43
44#[cfg(feature = "bincode")]
45#[derive(Debug, Clone, Deserialize, Serialize)]
46struct LegacyHttpResponse {
47 body: Vec<u8>,
48 #[cfg(feature = "http-headers-compat")]
49 headers: std::collections::HashMap<String, String>,
50 #[cfg(not(feature = "http-headers-compat"))]
51 headers: std::collections::HashMap<String, Vec<String>>,
52 status: u16,
53 url: Url,
54 version: HttpVersion,
55}
56
57#[cfg(feature = "bincode")]
58impl From<LegacyHttpResponse> for HttpResponse {
59 fn from(legacy: LegacyHttpResponse) -> Self {
60 #[cfg(feature = "http-headers-compat")]
61 let headers = HttpHeaders::Legacy(legacy.headers);
62 #[cfg(not(feature = "http-headers-compat"))]
63 let headers = HttpHeaders::Modern(legacy.headers);
64
65 HttpResponse {
66 body: legacy.body,
67 headers,
68 status: legacy.status,
69 url: legacy.url,
70 version: legacy.version,
71 metadata: None,
72 }
73 }
74}
75
76#[cfg(feature = "bincode")]
77impl From<HttpResponse> for LegacyHttpResponse {
78 fn from(response: HttpResponse) -> Self {
79 #[cfg(feature = "http-headers-compat")]
80 let headers = match response.headers {
81 HttpHeaders::Legacy(h) => h,
82 HttpHeaders::Modern(h) => {
83 h.into_iter().map(|(k, v)| (k, v.join(", "))).collect()
84 }
85 };
86 #[cfg(not(feature = "http-headers-compat"))]
87 let headers = match response.headers {
88 HttpHeaders::Modern(h) => h,
89 };
90
91 LegacyHttpResponse {
92 body: response.body,
93 headers,
94 status: response.status,
95 url: response.url,
96 version: response.version,
97 }
98 }
99}
100
101impl CACacheManager {
102 pub fn new(path: PathBuf, remove_fully: bool) -> Self {
104 Self {
105 path,
106 remove_opts: cacache::RemoveOpts::new().remove_fully(remove_fully),
107 }
108 }
109
110 pub async fn clear(&self) -> Result<()> {
112 cacache::clear(&self.path).await?;
113 Ok(())
114 }
115}
116
117impl CacheManager for CACacheManager {
118 async fn get(
119 &self,
120 cache_key: &str,
121 ) -> Result<Option<(HttpResponse, CachePolicy)>> {
122 let d = match cacache::read(&self.path, cache_key).await {
123 Ok(d) => d,
124 Err(_e) => {
125 return Ok(None);
126 }
127 };
128
129 #[cfg(feature = "postcard")]
133 {
134 match postcard::from_bytes::<Store>(&d) {
135 Ok(store) => Ok(Some((store.response, store.policy))),
136 Err(_e) => {
137 #[cfg(feature = "bincode")]
138 {
139 match bincode::deserialize::<BincodeStore>(&d) {
140 Ok(store) => {
141 return Ok(Some((
142 store.response.into(),
143 store.policy,
144 )));
145 }
146 Err(e) => {
147 log::debug!(
148 "Failed to deserialize cache entry for key '{}': {}",
149 cache_key,
150 e
151 );
152 return Ok(None);
153 }
154 }
155 }
156 #[cfg(not(feature = "bincode"))]
157 {
158 log::debug!(
159 "Failed to deserialize cache entry for key '{}': {}",
160 cache_key,
161 _e
162 );
163 Ok(None)
164 }
165 }
166 }
167 }
168 #[cfg(all(feature = "bincode", not(feature = "postcard")))]
169 {
170 match bincode::deserialize::<BincodeStore>(&d) {
171 Ok(store) => Ok(Some((store.response.into(), store.policy))),
172 Err(e) => {
173 log::debug!(
174 "Failed to deserialize cache entry for key '{}': {}",
175 cache_key,
176 e
177 );
178 Ok(None)
179 }
180 }
181 }
182 }
183
184 async fn put(
185 &self,
186 cache_key: String,
187 response: HttpResponse,
188 policy: CachePolicy,
189 ) -> Result<HttpResponse> {
190 #[cfg(feature = "postcard")]
193 let data = Store { response, policy };
194 #[cfg(all(feature = "bincode", not(feature = "postcard")))]
195 let data = BincodeStore { response: response.into(), policy };
196
197 #[cfg(feature = "postcard")]
198 let bytes = postcard::to_allocvec(&data)?;
199 #[cfg(all(feature = "bincode", not(feature = "postcard")))]
200 let bytes = bincode::serialize(&data)?;
201
202 cacache::write(&self.path, cache_key, bytes).await?;
203
204 #[cfg(feature = "postcard")]
205 {
206 Ok(data.response)
207 }
208 #[cfg(all(feature = "bincode", not(feature = "postcard")))]
209 {
210 Ok(data.response.into())
211 }
212 }
213
214 async fn delete(&self, cache_key: &str) -> Result<()> {
215 self.remove_opts.clone().remove(&self.path, cache_key).await?;
216 Ok(())
217 }
218}