1use std::{fmt::Debug, sync::Arc};
4
5use super::{Backend, BackendSession, ManageBackend};
6use crate::{
7 backend::OrderBy,
8 entry::{Entry, EntryKind, EntryOperation, EntryTag, Scan, TagFilter},
9 error::Error,
10 future::BoxFuture,
11 options::IntoOptions,
12 protect::{PassKey, StoreKeyMethod},
13};
14
15#[cfg(feature = "postgres")]
16use super::postgres;
17
18#[cfg(feature = "sqlite")]
19use super::sqlite;
20
21#[derive(Clone, Debug)]
23pub struct AnyBackend(Arc<dyn Backend<Session = AnyBackendSession>>);
24
25pub fn into_any_backend(inst: impl Backend + 'static) -> AnyBackend {
27 AnyBackend(Arc::new(WrapBackend(inst)))
28}
29
30#[derive(Debug)]
32struct WrapBackend<B: Backend>(B);
33
34impl<B: Backend> Backend for WrapBackend<B> {
35 type Session = AnyBackendSession;
36
37 #[inline]
38 fn create_profile(&self, name: Option<String>) -> BoxFuture<'_, Result<String, Error>> {
39 self.0.create_profile(name)
40 }
41
42 #[inline]
43 fn get_active_profile(&self) -> String {
44 self.0.get_active_profile()
45 }
46
47 #[inline]
48 fn get_default_profile(&self) -> BoxFuture<'_, Result<String, Error>> {
49 self.0.get_default_profile()
50 }
51
52 #[inline]
53 fn set_default_profile(&self, profile: String) -> BoxFuture<'_, Result<(), Error>> {
54 self.0.set_default_profile(profile)
55 }
56
57 #[inline]
58 fn list_profiles(&self) -> BoxFuture<'_, Result<Vec<String>, Error>> {
59 self.0.list_profiles()
60 }
61
62 #[inline]
63 fn remove_profile(&self, name: String) -> BoxFuture<'_, Result<bool, Error>> {
64 self.0.remove_profile(name)
65 }
66
67 #[inline]
68 fn rename_profile(
69 &self,
70 from_name: String,
71 to_name: String,
72 ) -> BoxFuture<'_, Result<bool, Error>> {
73 self.0.rename_profile(from_name, to_name)
74 }
75
76 #[inline]
77 fn scan(
78 &self,
79 profile: Option<String>,
80 kind: Option<EntryKind>,
81 category: Option<String>,
82 tag_filter: Option<TagFilter>,
83 offset: Option<i64>,
84 limit: Option<i64>,
85 order_by: Option<OrderBy>,
86 descending: bool,
87 ) -> BoxFuture<'_, Result<Scan<'static, Entry>, Error>> {
88 self.0.scan(
89 profile, kind, category, tag_filter, offset, limit, order_by, descending,
90 )
91 }
92
93 #[inline]
94 fn session(&self, profile: Option<String>, transaction: bool) -> Result<Self::Session, Error> {
95 Ok(AnyBackendSession(Box::new(
96 self.0.session(profile, transaction)?,
97 )))
98 }
99
100 #[inline]
101 fn rekey(
102 &mut self,
103 method: StoreKeyMethod,
104 key: PassKey<'_>,
105 ) -> BoxFuture<'_, Result<(), Error>> {
106 self.0.rekey(method, key)
107 }
108
109 #[inline]
110 fn close(&self) -> BoxFuture<'_, Result<(), Error>> {
111 self.0.close()
112 }
113}
114
115impl Backend for AnyBackend {
117 type Session = AnyBackendSession;
118
119 #[inline]
120 fn create_profile(&self, name: Option<String>) -> BoxFuture<'_, Result<String, Error>> {
121 self.0.create_profile(name)
122 }
123
124 #[inline]
125 fn get_active_profile(&self) -> String {
126 self.0.get_active_profile()
127 }
128
129 #[inline]
130 fn get_default_profile(&self) -> BoxFuture<'_, Result<String, Error>> {
131 self.0.get_default_profile()
132 }
133
134 #[inline]
135 fn set_default_profile(&self, profile: String) -> BoxFuture<'_, Result<(), Error>> {
136 self.0.set_default_profile(profile)
137 }
138
139 #[inline]
140 fn list_profiles(&self) -> BoxFuture<'_, Result<Vec<String>, Error>> {
141 self.0.list_profiles()
142 }
143
144 #[inline]
145 fn remove_profile(&self, name: String) -> BoxFuture<'_, Result<bool, Error>> {
146 self.0.remove_profile(name)
147 }
148
149 #[inline]
150 fn rename_profile(
151 &self,
152 from_name: String,
153 to_name: String,
154 ) -> BoxFuture<'_, Result<bool, Error>> {
155 self.0.rename_profile(from_name, to_name)
156 }
157
158 #[inline]
159 fn scan(
160 &self,
161 profile: Option<String>,
162 kind: Option<EntryKind>,
163 category: Option<String>,
164 tag_filter: Option<TagFilter>,
165 offset: Option<i64>,
166 limit: Option<i64>,
167 order_by: Option<OrderBy>,
168 descending: bool,
169 ) -> BoxFuture<'_, Result<Scan<'static, Entry>, Error>> {
170 self.0.scan(
171 profile, kind, category, tag_filter, offset, limit, order_by, descending,
172 )
173 }
174
175 #[inline]
176 fn session(&self, profile: Option<String>, transaction: bool) -> Result<Self::Session, Error> {
177 Ok(AnyBackendSession(Box::new(
178 self.0.session(profile, transaction)?,
179 )))
180 }
181
182 #[inline]
183 fn rekey(
184 &mut self,
185 method: StoreKeyMethod,
186 key: PassKey<'_>,
187 ) -> BoxFuture<'_, Result<(), Error>> {
188 match Arc::get_mut(&mut self.0) {
189 Some(inner) => inner.rekey(method, key),
190 None => Box::pin(std::future::ready(Err(err_msg!(
191 "Cannot re-key a store with multiple references"
192 )))),
193 }
194 }
195
196 #[inline]
197 fn close(&self) -> BoxFuture<'_, Result<(), Error>> {
198 self.0.close()
199 }
200}
201
202#[derive(Debug)]
204pub struct AnyBackendSession(Box<dyn BackendSession>);
205
206impl BackendSession for AnyBackendSession {
207 fn count<'q>(
209 &'q mut self,
210 kind: Option<EntryKind>,
211 category: Option<&'q str>,
212 tag_filter: Option<TagFilter>,
213 ) -> BoxFuture<'q, Result<i64, Error>> {
214 self.0.count(kind, category, tag_filter)
215 }
216
217 fn fetch<'q>(
219 &'q mut self,
220 kind: EntryKind,
221 category: &'q str,
222 name: &'q str,
223 for_update: bool,
224 ) -> BoxFuture<'q, Result<Option<Entry>, Error>> {
225 self.0.fetch(kind, category, name, for_update)
226 }
227
228 fn fetch_all<'q>(
230 &'q mut self,
231 kind: Option<EntryKind>,
232 category: Option<&'q str>,
233 tag_filter: Option<TagFilter>,
234 limit: Option<i64>,
235 order_by: Option<OrderBy>,
236 descending: bool,
237 for_update: bool,
238 ) -> BoxFuture<'q, Result<Vec<Entry>, Error>> {
239 self.0.fetch_all(
240 kind, category, tag_filter, limit, order_by, descending, for_update,
241 )
242 }
243
244 fn remove_all<'q>(
246 &'q mut self,
247 kind: Option<EntryKind>,
248 category: Option<&'q str>,
249 tag_filter: Option<TagFilter>,
250 ) -> BoxFuture<'q, Result<i64, Error>> {
251 self.0.remove_all(kind, category, tag_filter)
252 }
253
254 #[allow(clippy::too_many_arguments)]
256 fn update<'q>(
257 &'q mut self,
258 kind: EntryKind,
259 operation: EntryOperation,
260 category: &'q str,
261 name: &'q str,
262 value: Option<&'q [u8]>,
263 tags: Option<&'q [EntryTag]>,
264 expiry_ms: Option<i64>,
265 ) -> BoxFuture<'q, Result<(), Error>> {
266 self.0
267 .update(kind, operation, category, name, value, tags, expiry_ms)
268 }
269
270 fn ping(&mut self) -> BoxFuture<'_, Result<(), Error>> {
272 self.0.ping()
273 }
274
275 fn close(&mut self, commit: bool) -> BoxFuture<'_, Result<(), Error>> {
277 self.0.close(commit)
278 }
279}
280
281impl<'a> ManageBackend<'a> for &'a str {
282 type Backend = AnyBackend;
283
284 fn open_backend(
285 self,
286 method: Option<StoreKeyMethod>,
287 pass_key: PassKey<'a>,
288 profile: Option<String>,
289 ) -> BoxFuture<'a, Result<Self::Backend, Error>> {
290 Box::pin(async move {
291 let opts = self.into_options()?;
292 debug!("Open store with options: {:?}", &opts);
293
294 match opts.scheme.as_ref() {
295 #[cfg(feature = "postgres")]
296 "postgres" => {
297 let opts = postgres::PostgresStoreOptions::new(opts)?;
298 let mgr = opts.open(method, pass_key, profile).await?;
299 Ok(into_any_backend(mgr))
300 }
301
302 #[cfg(feature = "sqlite")]
303 "sqlite" => {
304 let opts = sqlite::SqliteStoreOptions::new(opts)?;
305 let mgr = opts.open(method, pass_key, profile).await?;
306 Ok(into_any_backend(mgr))
307 }
308
309 _ => Err(err_msg!(
310 Unsupported,
311 "Unsupported backend: {}",
312 &opts.scheme
313 )),
314 }
315 })
316 }
317
318 fn provision_backend(
319 self,
320 method: StoreKeyMethod,
321 pass_key: PassKey<'a>,
322 profile: Option<String>,
323 recreate: bool,
324 ) -> BoxFuture<'a, Result<Self::Backend, Error>> {
325 Box::pin(async move {
326 let opts = self.into_options()?;
327 debug!("Provision store with options: {:?}", &opts);
328
329 match opts.scheme.as_ref() {
330 #[cfg(feature = "postgres")]
331 "postgres" => {
332 let opts = postgres::PostgresStoreOptions::new(opts)?;
333 let mgr = opts.provision(method, pass_key, profile, recreate).await?;
334 Ok(into_any_backend(mgr))
335 }
336
337 #[cfg(feature = "sqlite")]
338 "sqlite" => {
339 let opts = sqlite::SqliteStoreOptions::new(opts)?;
340 let mgr = opts.provision(method, pass_key, profile, recreate).await?;
341 Ok(into_any_backend(mgr))
342 }
343
344 _ => Err(err_msg!(
345 Unsupported,
346 "Unsupported backend: {}",
347 &opts.scheme
348 )),
349 }
350 })
351 }
352
353 fn remove_backend(self) -> BoxFuture<'a, Result<bool, Error>> {
354 Box::pin(async move {
355 let opts = self.into_options()?;
356 debug!("Remove store with options: {:?}", &opts);
357
358 match opts.scheme.as_ref() {
359 #[cfg(feature = "postgres")]
360 "postgres" => {
361 let opts = postgres::PostgresStoreOptions::new(opts)?;
362 Ok(opts.remove().await?)
363 }
364
365 #[cfg(feature = "sqlite")]
366 "sqlite" => {
367 let opts = sqlite::SqliteStoreOptions::new(opts)?;
368 Ok(opts.remove().await?)
369 }
370
371 _ => Err(err_msg!(
372 Unsupported,
373 "Unsupported backend: {}",
374 &opts.scheme
375 )),
376 }
377 })
378 }
379}