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 scan(
69 &self,
70 profile: Option<String>,
71 kind: Option<EntryKind>,
72 category: Option<String>,
73 tag_filter: Option<TagFilter>,
74 offset: Option<i64>,
75 limit: Option<i64>,
76 order_by: Option<OrderBy>,
77 descending: bool,
78 ) -> BoxFuture<'_, Result<Scan<'static, Entry>, Error>> {
79 self.0.scan(
80 profile, kind, category, tag_filter, offset, limit, order_by, descending,
81 )
82 }
83
84 #[inline]
85 fn session(&self, profile: Option<String>, transaction: bool) -> Result<Self::Session, Error> {
86 Ok(AnyBackendSession(Box::new(
87 self.0.session(profile, transaction)?,
88 )))
89 }
90
91 #[inline]
92 fn rekey(
93 &mut self,
94 method: StoreKeyMethod,
95 key: PassKey<'_>,
96 ) -> BoxFuture<'_, Result<(), Error>> {
97 self.0.rekey(method, key)
98 }
99
100 #[inline]
101 fn close(&self) -> BoxFuture<'_, Result<(), Error>> {
102 self.0.close()
103 }
104}
105
106impl Backend for AnyBackend {
108 type Session = AnyBackendSession;
109
110 #[inline]
111 fn create_profile(&self, name: Option<String>) -> BoxFuture<'_, Result<String, Error>> {
112 self.0.create_profile(name)
113 }
114
115 #[inline]
116 fn get_active_profile(&self) -> String {
117 self.0.get_active_profile()
118 }
119
120 #[inline]
121 fn get_default_profile(&self) -> BoxFuture<'_, Result<String, Error>> {
122 self.0.get_default_profile()
123 }
124
125 #[inline]
126 fn set_default_profile(&self, profile: String) -> BoxFuture<'_, Result<(), Error>> {
127 self.0.set_default_profile(profile)
128 }
129
130 #[inline]
131 fn list_profiles(&self) -> BoxFuture<'_, Result<Vec<String>, Error>> {
132 self.0.list_profiles()
133 }
134
135 #[inline]
136 fn remove_profile(&self, name: String) -> BoxFuture<'_, Result<bool, Error>> {
137 self.0.remove_profile(name)
138 }
139
140 #[inline]
141 fn scan(
142 &self,
143 profile: Option<String>,
144 kind: Option<EntryKind>,
145 category: Option<String>,
146 tag_filter: Option<TagFilter>,
147 offset: Option<i64>,
148 limit: Option<i64>,
149 order_by: Option<OrderBy>,
150 descending: bool,
151 ) -> BoxFuture<'_, Result<Scan<'static, Entry>, Error>> {
152 self.0.scan(
153 profile, kind, category, tag_filter, offset, limit, order_by, descending,
154 )
155 }
156
157 #[inline]
158 fn session(&self, profile: Option<String>, transaction: bool) -> Result<Self::Session, Error> {
159 Ok(AnyBackendSession(Box::new(
160 self.0.session(profile, transaction)?,
161 )))
162 }
163
164 #[inline]
165 fn rekey(
166 &mut self,
167 method: StoreKeyMethod,
168 key: PassKey<'_>,
169 ) -> BoxFuture<'_, Result<(), Error>> {
170 match Arc::get_mut(&mut self.0) {
171 Some(inner) => inner.rekey(method, key),
172 None => Box::pin(std::future::ready(Err(err_msg!(
173 "Cannot re-key a store with multiple references"
174 )))),
175 }
176 }
177
178 #[inline]
179 fn close(&self) -> BoxFuture<'_, Result<(), Error>> {
180 self.0.close()
181 }
182}
183
184#[derive(Debug)]
186pub struct AnyBackendSession(Box<dyn BackendSession>);
187
188impl BackendSession for AnyBackendSession {
189 fn count<'q>(
191 &'q mut self,
192 kind: Option<EntryKind>,
193 category: Option<&'q str>,
194 tag_filter: Option<TagFilter>,
195 ) -> BoxFuture<'q, Result<i64, Error>> {
196 self.0.count(kind, category, tag_filter)
197 }
198
199 fn fetch<'q>(
201 &'q mut self,
202 kind: EntryKind,
203 category: &'q str,
204 name: &'q str,
205 for_update: bool,
206 ) -> BoxFuture<'q, Result<Option<Entry>, Error>> {
207 self.0.fetch(kind, category, name, for_update)
208 }
209
210 fn fetch_all<'q>(
212 &'q mut self,
213 kind: Option<EntryKind>,
214 category: Option<&'q str>,
215 tag_filter: Option<TagFilter>,
216 limit: Option<i64>,
217 order_by: Option<OrderBy>,
218 descending: bool,
219 for_update: bool,
220 ) -> BoxFuture<'q, Result<Vec<Entry>, Error>> {
221 self.0.fetch_all(
222 kind, category, tag_filter, limit, order_by, descending, for_update,
223 )
224 }
225
226 fn remove_all<'q>(
228 &'q mut self,
229 kind: Option<EntryKind>,
230 category: Option<&'q str>,
231 tag_filter: Option<TagFilter>,
232 ) -> BoxFuture<'q, Result<i64, Error>> {
233 self.0.remove_all(kind, category, tag_filter)
234 }
235
236 #[allow(clippy::too_many_arguments)]
238 fn update<'q>(
239 &'q mut self,
240 kind: EntryKind,
241 operation: EntryOperation,
242 category: &'q str,
243 name: &'q str,
244 value: Option<&'q [u8]>,
245 tags: Option<&'q [EntryTag]>,
246 expiry_ms: Option<i64>,
247 ) -> BoxFuture<'q, Result<(), Error>> {
248 self.0
249 .update(kind, operation, category, name, value, tags, expiry_ms)
250 }
251
252 fn ping(&mut self) -> BoxFuture<'_, Result<(), Error>> {
254 self.0.ping()
255 }
256
257 fn close(&mut self, commit: bool) -> BoxFuture<'_, Result<(), Error>> {
259 self.0.close(commit)
260 }
261}
262
263impl<'a> ManageBackend<'a> for &'a str {
264 type Backend = AnyBackend;
265
266 fn open_backend(
267 self,
268 method: Option<StoreKeyMethod>,
269 pass_key: PassKey<'a>,
270 profile: Option<String>,
271 ) -> BoxFuture<'a, Result<Self::Backend, Error>> {
272 Box::pin(async move {
273 let opts = self.into_options()?;
274 debug!("Open store with options: {:?}", &opts);
275
276 match opts.scheme.as_ref() {
277 #[cfg(feature = "postgres")]
278 "postgres" => {
279 let opts = postgres::PostgresStoreOptions::new(opts)?;
280 let mgr = opts.open(method, pass_key, profile).await?;
281 Ok(into_any_backend(mgr))
282 }
283
284 #[cfg(feature = "sqlite")]
285 "sqlite" => {
286 let opts = sqlite::SqliteStoreOptions::new(opts)?;
287 let mgr = opts.open(method, pass_key, profile).await?;
288 Ok(into_any_backend(mgr))
289 }
290
291 _ => Err(err_msg!(
292 Unsupported,
293 "Unsupported backend: {}",
294 &opts.scheme
295 )),
296 }
297 })
298 }
299
300 fn provision_backend(
301 self,
302 method: StoreKeyMethod,
303 pass_key: PassKey<'a>,
304 profile: Option<String>,
305 recreate: bool,
306 ) -> BoxFuture<'a, Result<Self::Backend, Error>> {
307 Box::pin(async move {
308 let opts = self.into_options()?;
309 debug!("Provision store with options: {:?}", &opts);
310
311 match opts.scheme.as_ref() {
312 #[cfg(feature = "postgres")]
313 "postgres" => {
314 let opts = postgres::PostgresStoreOptions::new(opts)?;
315 let mgr = opts.provision(method, pass_key, profile, recreate).await?;
316 Ok(into_any_backend(mgr))
317 }
318
319 #[cfg(feature = "sqlite")]
320 "sqlite" => {
321 let opts = sqlite::SqliteStoreOptions::new(opts)?;
322 let mgr = opts.provision(method, pass_key, profile, recreate).await?;
323 Ok(into_any_backend(mgr))
324 }
325
326 _ => Err(err_msg!(
327 Unsupported,
328 "Unsupported backend: {}",
329 &opts.scheme
330 )),
331 }
332 })
333 }
334
335 fn remove_backend(self) -> BoxFuture<'a, Result<bool, Error>> {
336 Box::pin(async move {
337 let opts = self.into_options()?;
338 debug!("Remove store with options: {:?}", &opts);
339
340 match opts.scheme.as_ref() {
341 #[cfg(feature = "postgres")]
342 "postgres" => {
343 let opts = postgres::PostgresStoreOptions::new(opts)?;
344 Ok(opts.remove().await?)
345 }
346
347 #[cfg(feature = "sqlite")]
348 "sqlite" => {
349 let opts = sqlite::SqliteStoreOptions::new(opts)?;
350 Ok(opts.remove().await?)
351 }
352
353 _ => Err(err_msg!(
354 Unsupported,
355 "Unsupported backend: {}",
356 &opts.scheme
357 )),
358 }
359 })
360 }
361}