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
101#[allow(dead_code)]
102impl CACacheManager {
103 pub fn new(path: PathBuf, remove_fully: bool) -> Self {
105 Self {
106 path,
107 remove_opts: cacache::RemoveOpts::new().remove_fully(remove_fully),
108 }
109 }
110
111 pub async fn clear(&self) -> Result<()> {
113 cacache::clear(&self.path).await?;
114 Ok(())
115 }
116}
117
118#[async_trait::async_trait]
119impl CacheManager for CACacheManager {
120 async fn get(
121 &self,
122 cache_key: &str,
123 ) -> Result<Option<(HttpResponse, CachePolicy)>> {
124 let d = match cacache::read(&self.path, cache_key).await {
125 Ok(d) => d,
126 Err(_e) => {
127 return Ok(None);
128 }
129 };
130
131 #[cfg(feature = "postcard")]
135 {
136 match postcard::from_bytes::<Store>(&d) {
137 Ok(store) => return Ok(Some((store.response, store.policy))),
138 Err(_e) => {
139 #[cfg(feature = "bincode")]
140 {
141 match bincode::deserialize::<BincodeStore>(&d) {
142 Ok(store) => {
143 return Ok(Some((
144 store.response.into(),
145 store.policy,
146 )));
147 }
148 Err(e) => {
149 log::warn!(
150 "Failed to deserialize cache entry for key '{}': {}",
151 cache_key,
152 e
153 );
154 return Ok(None);
155 }
156 }
157 }
158 #[cfg(not(feature = "bincode"))]
159 {
160 log::warn!(
161 "Failed to deserialize cache entry for key '{}': {}",
162 cache_key,
163 _e
164 );
165 return Ok(None);
166 }
167 }
168 }
169 }
170 #[cfg(all(feature = "bincode", not(feature = "postcard")))]
171 {
172 match bincode::deserialize::<BincodeStore>(&d) {
173 Ok(store) => Ok(Some((store.response.into(), store.policy))),
174 Err(e) => {
175 log::warn!(
176 "Failed to deserialize cache entry for key '{}': {}",
177 cache_key,
178 e
179 );
180 Ok(None)
181 }
182 }
183 }
184 }
185
186 async fn put(
187 &self,
188 cache_key: String,
189 response: HttpResponse,
190 policy: CachePolicy,
191 ) -> Result<HttpResponse> {
192 #[cfg(feature = "postcard")]
195 let data = Store { response, policy };
196 #[cfg(all(feature = "bincode", not(feature = "postcard")))]
197 let data = BincodeStore { response: response.into(), policy };
198
199 #[cfg(feature = "postcard")]
200 let bytes = postcard::to_allocvec(&data)?;
201 #[cfg(all(feature = "bincode", not(feature = "postcard")))]
202 let bytes = bincode::serialize(&data)?;
203
204 cacache::write(&self.path, cache_key, bytes).await?;
205
206 #[cfg(feature = "postcard")]
207 {
208 Ok(data.response)
209 }
210 #[cfg(all(feature = "bincode", not(feature = "postcard")))]
211 {
212 Ok(data.response.into())
213 }
214 }
215
216 async fn delete(&self, cache_key: &str) -> Result<()> {
217 self.remove_opts.clone().remove(&self.path, cache_key).await?;
218 Ok(())
219 }
220}