1mod file;
9mod memory;
10
11use crate::Cacheable;
12use crate::error::BackendError;
13use async_trait::async_trait;
14use futures::future::BoxFuture;
15use futures::stream::BoxStream;
16use serde::{Serialize, de::DeserializeOwned};
17use std::sync::Arc;
18use std::time::Duration;
19
20pub use file::FileBackend;
21pub use memory::MemoryBackend;
22
23pub type BackendInvalidationStream<Id> =
25 BoxStream<'static, Result<BackendInvalidation<Id>, BackendError>>;
26
27#[derive(Debug, Clone, PartialEq, Eq)]
35pub struct BackendKeyspace {
36 pub namespace: Option<Arc<str>>,
38 pub type_name: &'static str,
40}
41
42impl BackendKeyspace {
43 pub(crate) fn for_type<T: Cacheable>(namespace: Option<&str>) -> Self {
45 Self {
46 namespace: namespace.map(Arc::from),
47 type_name: T::cache_type_name(),
48 }
49 }
50}
51
52#[derive(Debug, Clone, PartialEq, Eq, Serialize, serde::Deserialize)]
54pub enum BackendInvalidation<Id> {
55 Id(Id),
57 All,
59}
60
61#[async_trait]
67pub trait CacheBackend<T>: Send + Sync
68where
69 T: Cacheable + Serialize + DeserializeOwned,
70 T::Id: Serialize + DeserializeOwned,
71{
72 async fn get(&self, keyspace: &BackendKeyspace, id: &T::Id) -> Result<Option<T>, BackendError>;
74
75 async fn put(
77 &self,
78 keyspace: &BackendKeyspace,
79 id: &T::Id,
80 value: &T,
81 ttl: Option<Duration>,
82 ) -> Result<(), BackendError>;
83
84 async fn invalidate(&self, keyspace: &BackendKeyspace, id: &T::Id) -> Result<(), BackendError>;
86
87 async fn invalidate_all(&self, keyspace: &BackendKeyspace) -> Result<(), BackendError>;
94
95 fn invalidation_stream(&self, _keyspace: BackendKeyspace) -> BackendInvalidationStream<T::Id> {
97 Box::pin(futures::stream::empty())
98 }
99}
100
101pub(crate) trait BackendRuntime<T: Cacheable>: Send + Sync {
102 fn get<'a>(
103 &'a self,
104 keyspace: &'a BackendKeyspace,
105 id: &'a T::Id,
106 ) -> BoxFuture<'a, Result<Option<T>, BackendError>>;
107
108 fn put<'a>(
109 &'a self,
110 keyspace: &'a BackendKeyspace,
111 id: &'a T::Id,
112 value: &'a T,
113 ttl: Option<Duration>,
114 ) -> BoxFuture<'a, Result<(), BackendError>>;
115
116 fn invalidate<'a>(
117 &'a self,
118 keyspace: &'a BackendKeyspace,
119 id: &'a T::Id,
120 ) -> BoxFuture<'a, Result<(), BackendError>>;
121
122 fn invalidation_stream(&self, keyspace: BackendKeyspace) -> BackendInvalidationStream<T::Id>;
123}
124
125struct BackendRuntimeAdapter<B> {
126 backend: B,
127}
128
129pub(crate) fn erase_backend<T, B>(backend: B) -> Arc<dyn BackendRuntime<T>>
130where
131 T: Cacheable + Serialize + DeserializeOwned,
132 T::Id: Serialize + DeserializeOwned,
133 B: CacheBackend<T> + 'static,
134{
135 Arc::new(BackendRuntimeAdapter { backend })
136}
137
138impl<T, B> BackendRuntime<T> for BackendRuntimeAdapter<B>
139where
140 T: Cacheable + Serialize + DeserializeOwned,
141 T::Id: Serialize + DeserializeOwned,
142 B: CacheBackend<T>,
143{
144 fn get<'a>(
145 &'a self,
146 keyspace: &'a BackendKeyspace,
147 id: &'a T::Id,
148 ) -> BoxFuture<'a, Result<Option<T>, BackendError>> {
149 Box::pin(self.backend.get(keyspace, id))
150 }
151
152 fn put<'a>(
153 &'a self,
154 keyspace: &'a BackendKeyspace,
155 id: &'a T::Id,
156 value: &'a T,
157 ttl: Option<Duration>,
158 ) -> BoxFuture<'a, Result<(), BackendError>> {
159 Box::pin(self.backend.put(keyspace, id, value, ttl))
160 }
161
162 fn invalidate<'a>(
163 &'a self,
164 keyspace: &'a BackendKeyspace,
165 id: &'a T::Id,
166 ) -> BoxFuture<'a, Result<(), BackendError>> {
167 Box::pin(self.backend.invalidate(keyspace, id))
168 }
169
170 fn invalidation_stream(&self, keyspace: BackendKeyspace) -> BackendInvalidationStream<T::Id> {
171 self.backend.invalidation_stream(keyspace)
172 }
173}
174
175pub(crate) fn keyspace_storage_key<T>(
176 keyspace: &BackendKeyspace,
177 id: &T::Id,
178) -> Result<String, BackendError>
179where
180 T: Cacheable,
181 T::Id: Serialize,
182{
183 let id_json = serde_json::to_vec(id)?;
184 let id_part = format!("id_{}", encode_hex(&id_json));
185 Ok(format!(
186 "{}{}",
187 keyspace_storage_key_prefix(keyspace),
188 id_part
189 ))
190}
191
192pub(crate) fn keyspace_storage_key_prefix(keyspace: &BackendKeyspace) -> String {
193 let namespace = match &keyspace.namespace {
194 Some(ns) => format!("ns_{}", encode_hex(ns.as_bytes())),
195 None => "ns_none".to_owned(),
196 };
197 let type_part = format!("ty_{}", encode_hex(keyspace.type_name.as_bytes()));
198 format!("{namespace}/{type_part}/")
199}
200
201pub(crate) fn encode_hex(bytes: &[u8]) -> String {
202 const HEX: &[u8; 16] = b"0123456789abcdef";
203 let mut encoded = String::with_capacity(bytes.len() * 2);
204 for &byte in bytes {
205 encoded.push(HEX[(byte >> 4) as usize] as char);
206 encoded.push(HEX[(byte & 0x0f) as usize] as char);
207 }
208 encoded
209}