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(all(feature = "bincode", not(feature = "postcard")))]
35#[derive(Debug, Deserialize, Serialize)]
36struct Store {
37 response: LegacyHttpResponse,
38 policy: CachePolicy,
39}
40
41#[cfg(all(feature = "bincode", not(feature = "postcard")))]
42use crate::{HttpHeaders, HttpVersion, Url};
43
44#[cfg(all(feature = "bincode", not(feature = "postcard")))]
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(all(feature = "bincode", not(feature = "postcard")))]
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(all(feature = "bincode", not(feature = "postcard")))]
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 store: Store = match cacache::read(&self.path, cache_key).await {
125 Ok(d) => {
126 #[cfg(feature = "postcard")]
127 {
128 postcard::from_bytes(&d)?
129 }
130 #[cfg(all(feature = "bincode", not(feature = "postcard")))]
131 {
132 bincode::deserialize(&d)?
133 }
134 }
135 Err(_e) => {
136 return Ok(None);
137 }
138 };
139
140 #[cfg(feature = "postcard")]
141 {
142 Ok(Some((store.response, store.policy)))
143 }
144 #[cfg(all(feature = "bincode", not(feature = "postcard")))]
145 {
146 Ok(Some((store.response.into(), store.policy)))
147 }
148 }
149
150 async fn put(
151 &self,
152 cache_key: String,
153 response: HttpResponse,
154 policy: CachePolicy,
155 ) -> Result<HttpResponse> {
156 #[cfg(feature = "postcard")]
157 let data = Store { response, policy };
158 #[cfg(all(feature = "bincode", not(feature = "postcard")))]
159 let data = Store { response: response.into(), policy };
160
161 #[cfg(feature = "postcard")]
162 let bytes = postcard::to_allocvec(&data)?;
163 #[cfg(all(feature = "bincode", not(feature = "postcard")))]
164 let bytes = bincode::serialize(&data)?;
165
166 cacache::write(&self.path, cache_key, bytes).await?;
167
168 #[cfg(feature = "postcard")]
169 {
170 Ok(data.response)
171 }
172 #[cfg(all(feature = "bincode", not(feature = "postcard")))]
173 {
174 Ok(data.response.into())
175 }
176 }
177
178 async fn delete(&self, cache_key: &str) -> Result<()> {
179 self.remove_opts.clone().remove(&self.path, cache_key).await?;
180 Ok(())
181 }
182}