actix_storage/
storage.rs

1use std::convert::AsRef;
2use std::future::{ready, Ready};
3use std::sync::Arc;
4use std::time::Duration;
5
6use actix_web::{dev::Payload, error::ErrorInternalServerError, FromRequest, HttpRequest};
7
8use crate::dev::{Expiry, ExpiryStore, Store};
9use crate::error::Result;
10
11#[cfg(feature = "with-serde")]
12use crate::format::{deserialize, serialize, Format};
13
14pub const GLOBAL_SCOPE: [u8; 20] = *b"STORAGE_GLOBAL_SCOPE";
15
16/// Takes the underlying backend and provides common methods for it
17///
18/// It can be stored in actix_web's Data and be used from handlers
19/// without specifying the backend itself and provides all the common methods from underlying
20/// store and expiry.
21/// The backend this struct holds should implement [`ExpiryStore`](dev/trait.ExpiryStore.html)
22/// either directly, or by depending on the default polyfill.
23/// Look [`StorageBuilder`](dev/struct.StorageBuilder.html) for more details.
24///
25/// ## Example
26///
27/// ```rust
28/// use actix_storage::Storage;
29/// use actix_web::*;
30///
31/// async fn index(storage: Storage) -> Result<String, Error>{
32///     storage.set_bytes("key", "value").await;
33///     let val = storage.get_bytes("key").await?.unwrap_or_default();
34///     Ok(std::str::from_utf8(&val)
35///         .map_err(|err| error::ErrorInternalServerError("Storage error"))?.to_string())
36/// }
37/// ```
38///
39/// It is also possible to set and get values directly using serde by enabling
40/// `with-serde` feature flag.
41///
42#[derive(Clone)]
43pub struct Storage {
44    scope: Arc<[u8]>,
45    store: Arc<dyn ExpiryStore>,
46    #[cfg(feature = "with-serde")]
47    format: Format,
48}
49
50impl Storage {
51    /// Returns the storage builder struct
52    pub fn build() -> StorageBuilder {
53        StorageBuilder::default()
54    }
55
56    /// Return a new Storage struct for the specified scope.
57    ///
58    /// Scopes may or may not be implemented as key prefixes but should provide
59    /// some guarantees to not mutate other scopes.
60    ///
61    /// ## Example
62    /// ```rust
63    /// # use actix_storage::Storage;
64    /// # use actix_web::*;
65    /// #
66    /// # async fn index<'a>(storage: Storage) -> &'a str {
67    /// let cache = storage.scope("cache");
68    /// cache.set("age", &60_u8).await;
69    /// #     "set"
70    /// # }
71    /// ```
72    pub fn scope(&self, scope: impl AsRef<[u8]>) -> Storage {
73        Storage {
74            scope: scope.as_ref().into(),
75            store: self.store.clone(),
76            #[cfg(feature = "with-serde")]
77            format: self.format,
78        }
79    }
80
81    /// Stores a generic serializable value on storage using serde
82    ///
83    /// Calling set operations twice on the same key, overwrites it's value and
84    /// clear the expiry on that key(if it exist).
85    ///
86    /// ## Example
87    /// ```rust
88    /// # use actix_storage::Storage;
89    /// # use actix_web::*;
90    /// #
91    /// # async fn index<'a>(storage: Storage) -> &'a str {
92    /// storage.set("age", &60_u8).await;
93    /// #     "set"
94    /// # }
95    /// ```
96    ///
97    /// ## Errors
98    /// Beside the normal errors caused by the storage itself, it will result in error if
99    /// serialization fails.
100    ///
101    /// Note: it required the value to be `Sized` as some of the serde extensions currently
102    /// has the same requirement, this restriction may be lifted in future.
103    ///
104    /// requires `"with-serde"` feature and one of the format features to work ex. `"serde-json"`
105    #[cfg(feature = "with-serde")]
106    pub async fn set<V>(&self, key: impl AsRef<[u8]>, value: &V) -> Result<()>
107    where
108        V: serde::Serialize,
109    {
110        self.store
111            .set(
112                self.scope.clone(),
113                key.as_ref().into(),
114                serialize(value, &self.format)?.into(),
115            )
116            .await
117    }
118
119    /// Stores a generic serializable value on storage using serde and sets expiry on the key
120    /// It should be prefered over explicity setting a value and putting an expiry on it as
121    /// providers may provide a more optimized way to do both operations at once.
122    ///
123    /// Calling set operations twice on the same key, overwrites it's value and
124    /// clear the expiry on that key(if it exist).
125    ///
126    /// ## Example
127    /// ```rust
128    /// # use actix_storage::Storage;
129    /// # use actix_web::*;
130    /// # use std::time::Duration;
131    /// #
132    /// # async fn index<'a>(storage: Storage) -> &'a str {
133    /// storage.set_expiring("age", &60_u8, Duration::from_secs(10)).await;
134    /// #     "set"
135    /// # }
136    /// ```
137    ///
138    /// ## Errors
139    /// Beside the normal errors caused by the storage itself, it will result in error if
140    /// expiry provider is not set or serialization fails.
141    ///
142    /// Note: it required the value to be `Sized` as some of the serde extensions currently
143    /// has the same requirement, this restriction may be lifted in future.
144    ///
145    /// requires `"with-serde"` feature and one of the format features to work ex. `"serde-json"`
146    #[cfg(feature = "with-serde")]
147    pub async fn set_expiring<V>(
148        &self,
149        key: impl AsRef<[u8]>,
150        value: &V,
151        expires_in: Duration,
152    ) -> Result<()>
153    where
154        V: serde::Serialize,
155    {
156        self.store
157            .set_expiring(
158                self.scope.clone(),
159                key.as_ref().into(),
160                serialize(value, &self.format)?.into(),
161                expires_in,
162            )
163            .await
164    }
165
166    /// Stores a sequence of bytes on storage
167    ///
168    /// Calling set operations twice on the same key, overwrites it's value and
169    /// clear the expiry on that key(if it exist).
170    ///
171    /// ## Example
172    /// ```rust
173    /// # use actix_storage::Storage;
174    /// # use actix_web::*;
175    /// #
176    /// # async fn index<'a>(storage: Storage) -> &'a str {
177    /// storage.set_bytes("age", vec![10]).await;
178    /// storage.set_bytes("name", "Violet".as_bytes()).await;
179    /// #     "set"
180    /// # }
181    /// ```
182    pub async fn set_bytes(&self, key: impl AsRef<[u8]>, value: impl AsRef<[u8]>) -> Result<()> {
183        self.store
184            .set(
185                self.scope.clone(),
186                key.as_ref().into(),
187                value.as_ref().into(),
188            )
189            .await
190    }
191
192    /// Stores a sequence of bytes on storage and sets expiry on the key
193    /// It should be prefered over calling set and expire as providers may define
194    /// a more optimized way to do both operations at once.
195    ///
196    /// Calling set operations twice on the same key, overwrites it's value and
197    /// clear the expiry on that key(if it exist).
198    ///
199    /// ## Example
200    /// ```rust
201    /// # use actix_storage::Storage;
202    /// # use actix_web::*;
203    /// # use std::time::Duration;
204    /// #
205    /// # async fn index<'a>(storage: Storage) -> &'a str {
206    /// storage.set_expiring_bytes("name", "Violet".as_bytes(), Duration::from_secs(10)).await;
207    /// #     "set"
208    /// # }
209    /// ```
210    ///
211    /// ## Errors
212    /// Beside the normal errors caused by the storage itself, it will result in error if
213    /// expiry provider is not set.
214    pub async fn set_expiring_bytes(
215        &self,
216        key: impl AsRef<[u8]>,
217        value: impl AsRef<[u8]>,
218        expires_in: Duration,
219    ) -> Result<()> {
220        self.store
221            .set_expiring(
222                self.scope.clone(),
223                key.as_ref().into(),
224                value.as_ref().into(),
225                expires_in,
226            )
227            .await
228    }
229
230    /// Gets a generic deserializable value from backend using serde
231    ///
232    /// ## Example
233    /// ```rust
234    /// # use actix_storage::Storage;
235    /// # use actix_web::*;
236    /// #
237    /// # async fn index(storage: Storage) -> Result<String, Error> {
238    /// let val: Option<String> = storage.get("key").await?;
239    /// #     Ok(val.unwrap())
240    /// # }
241    /// ```
242    ///
243    /// ## Errors
244    /// Beside the normal errors caused by the storage itself, it will result in error if
245    /// deserialization fails.
246    ///
247    /// requires `"with-serde"` feature and one of the format features to work ex. `"serde-json"`
248    #[cfg(feature = "with-serde")]
249    pub async fn get<K, V>(&self, key: K) -> Result<Option<V>>
250    where
251        K: AsRef<[u8]>,
252        V: serde::de::DeserializeOwned,
253    {
254        let val = self
255            .store
256            .get(self.scope.clone(), key.as_ref().into())
257            .await?;
258        val.map(|val| deserialize(val.as_ref(), &self.format))
259            .transpose()
260    }
261
262    /// Gets a generic deserializable value from backend using serde together with its expiry
263    /// It should be prefered over calling get and expiry as providers may define
264    /// a more optimized way to do the both operations at once.
265    ///
266    /// ## Example
267    /// ```rust
268    /// # use actix_storage::Storage;
269    /// # use actix_web::*;
270    /// #
271    /// # async fn index(storage: Storage) -> Result<String> {
272    /// let val: Option<(String, _)> = storage.get_expiring("key").await?;
273    /// #     Ok(val.unwrap().0)
274    /// # }
275    /// ```
276    ///
277    /// ## Errors
278    /// Beside the normal errors caused by the storage itself, it will result in error if
279    /// expiry provider is not set or deserialization fails.
280    ///
281    /// requires `"with-serde"` and one of the format features to work ex. `"serde-json"`
282    #[cfg(feature = "with-serde")]
283    pub async fn get_expiring<K, V>(&self, key: K) -> Result<Option<(V, Option<Duration>)>>
284    where
285        K: AsRef<[u8]>,
286        V: serde::de::DeserializeOwned,
287    {
288        if let Some((val, expiry)) = self
289            .store
290            .get_expiring(self.scope.clone(), key.as_ref().into())
291            .await?
292        {
293            let val = deserialize(val.as_ref(), &self.format)?;
294            Ok(Some((val, expiry)))
295        } else {
296            Ok(None)
297        }
298    }
299
300    /// Gets a sequence of bytes from backend, resulting in an owned vector
301    ///
302    /// ## Example
303    /// ```rust
304    /// # use actix_storage::Storage;
305    /// # use actix_web::*;
306    /// #
307    /// # async fn index(storage: Storage) -> Result<String, Error> {
308    /// let val = storage.get_bytes("key").await?;
309    /// #     Ok(std::str::from_utf8(&val.unwrap()).unwrap_or_default().to_owned())
310    /// # }
311    /// ```
312    pub async fn get_bytes(&self, key: impl AsRef<[u8]>) -> Result<Option<Vec<u8>>> {
313        Ok(self
314            .store
315            .get(self.scope.clone(), key.as_ref().into())
316            .await?
317            .map(|val| {
318                let mut new_value = vec![];
319                new_value.extend_from_slice(val.as_ref());
320                new_value
321            }))
322    }
323
324    /// Same as `get_bytes` but it also gets expiry.
325    ///
326    /// ## Example
327    /// ```rust
328    /// # use actix_storage::Storage;
329    /// # use actix_web::*;
330    /// #
331    /// # async fn index(storage: Storage) -> Result<String, Error> {
332    /// let val = storage.get_expiring_bytes("key").await?;
333    /// #     Ok(std::str::from_utf8(&val.unwrap().0).unwrap_or_default().to_owned())
334    /// # }
335    /// ```
336    pub async fn get_expiring_bytes(
337        &self,
338        key: impl AsRef<[u8]>,
339    ) -> Result<Option<(Vec<u8>, Option<Duration>)>> {
340        if let Some((val, expiry)) = self
341            .store
342            .get_expiring(self.scope.clone(), key.as_ref().into())
343            .await?
344        {
345            Ok(Some((val.as_ref().into(), expiry)))
346        } else {
347            Ok(None)
348        }
349    }
350
351    /// Gets a sequence of bytes from backend, resulting in an arc
352    ///
353    /// ## Example
354    /// ```rust
355    /// # use actix_storage::Storage;
356    /// # use actix_web::*;
357    /// #
358    /// # async fn index(storage: Storage) -> Result<String, Error> {
359    /// let val = storage.get_bytes_ref("key").await?;
360    /// #     Ok(std::str::from_utf8(&val.unwrap()).unwrap_or_default().to_owned())
361    /// # }
362    /// ```
363    pub async fn get_bytes_ref(&self, key: impl AsRef<[u8]>) -> Result<Option<Arc<[u8]>>> {
364        self.store
365            .get(self.scope.clone(), key.as_ref().into())
366            .await
367    }
368
369    /// Same as `get_bytes_ref` but it also gets expiry.
370    ///
371    /// ## Example
372    /// ```rust
373    /// # use actix_storage::Storage;
374    /// # use actix_web::*;
375    /// #
376    /// # async fn index(storage: Storage) -> Result<String, Error> {
377    /// let val = storage.get_expiring_bytes_ref("key").await?;
378    /// #     Ok(std::str::from_utf8(&val.unwrap().0).unwrap_or_default().to_owned())
379    /// # }
380    /// ```
381    pub async fn get_expiring_bytes_ref(
382        &self,
383        key: impl AsRef<[u8]>,
384    ) -> Result<Option<(Arc<[u8]>, Option<Duration>)>> {
385        if let Some((val, expiry)) = self
386            .store
387            .get_expiring(self.scope.clone(), key.as_ref().into())
388            .await?
389        {
390            Ok(Some((val, expiry)))
391        } else {
392            Ok(None)
393        }
394    }
395
396    /// Deletes/Removes a key value pair from storage.
397    ///
398    /// ## Example
399    /// ```rust
400    /// # use actix_storage::Storage;
401    /// # use actix_web::*;
402    /// #
403    /// # async fn index(storage: Storage) -> Result<String, Error> {
404    /// storage.delete("key").await?;
405    /// #     Ok("deleted".to_string())
406    /// # }
407    /// ```
408    pub async fn delete(&self, key: impl AsRef<[u8]>) -> Result<()> {
409        self.store
410            .delete(self.scope.clone(), key.as_ref().into())
411            .await
412    }
413
414    /// Checks if storage contains a key.
415    ///
416    /// ## Example
417    /// ```rust
418    /// # use actix_storage::Storage;
419    /// # use actix_web::*;
420    /// #
421    /// # async fn index(storage: Storage) -> Result<String, Error> {
422    /// let exist = storage.contains_key("key").await?;
423    /// #     Ok("deleted".to_string())
424    /// # }
425    /// ```
426    pub async fn contains_key(&self, key: impl AsRef<[u8]>) -> Result<bool> {
427        self.store
428            .contains_key(self.scope.clone(), key.as_ref().into())
429            .await
430    }
431
432    /// Sets expiry on a key, it won't result in error if the key doesn't exist.
433    ///
434    /// Calling set methods twice or calling persist will result in expiry being erased
435    /// from the key, calling expire itself twice will overwrite the expiry for key.
436    ///
437    /// ## Example
438    /// ```rust
439    /// # use actix_storage::Storage;
440    /// # use actix_web::*;
441    /// # use std::time::Duration;
442    /// #
443    /// # async fn index(storage: Storage) -> Result<String, Error> {
444    /// storage.expire("key", Duration::from_secs(10)).await?;
445    /// #     Ok("deleted".to_string())
446    /// # }
447    /// ```
448    pub async fn expire(&self, key: impl AsRef<[u8]>, expire_in: Duration) -> Result<()> {
449        self.store
450            .expire(self.scope.clone(), key.as_ref().into(), expire_in)
451            .await
452    }
453
454    /// Gets expiry for the provided key, it will give none if there is no expiry set.
455    ///
456    /// The result of this method is not guaranteed to be exact and may be inaccurate
457    /// depending on sotrage implementation.
458    ///
459    /// ## Example
460    /// ```rust
461    /// # use actix_storage::Storage;
462    /// # use actix_web::*;
463    /// # use std::time::Duration;
464    /// #
465    /// # async fn index(storage: Storage) -> Result<String, Error> {
466    /// let exp = storage.expiry("key").await?;
467    /// if let Some(exp) = exp{
468    ///     println!("Key will expire in {} seconds", exp.as_secs());
469    /// } else {
470    ///     println!("Long live the key");
471    /// }
472    /// #     Ok("deleted".to_string())
473    /// # }
474    /// ```
475    pub async fn expiry(&self, key: impl AsRef<[u8]>) -> Result<Option<Duration>> {
476        self.store
477            .expiry(self.scope.clone(), key.as_ref().into())
478            .await
479    }
480
481    /// Extends expiry for a key, it won't result in error if the key doesn't exist.
482    ///
483    /// If the provided key doesn't have an expiry set, it will set the expiry on that key.
484    ///
485    /// ## Example
486    /// ```rust
487    /// # use actix_storage::Storage;
488    /// # use actix_web::*;
489    /// # use std::time::Duration;
490    /// #
491    /// # async fn index(storage: Storage) -> Result<String, Error> {
492    /// storage.expire("key", Duration::from_secs(5)).await?;
493    /// storage.extend("key", Duration::from_secs(5)).await?; // ket will expire in ~10 seconds
494    /// #     Ok("deleted".to_string())
495    /// # }
496    /// ```
497    pub async fn extend(&self, key: impl AsRef<[u8]>, expire_in: Duration) -> Result<()> {
498        self.store
499            .extend(self.scope.clone(), key.as_ref().into(), expire_in)
500            .await
501    }
502
503    /// Clears expiry from the provided key, making it persistant.
504    ///
505    /// Calling expire will overwrite persist.
506    ///
507    /// ## Example
508    /// ```rust
509    /// # use actix_storage::Storage;
510    /// # use actix_web::*;
511    /// # use std::time::Duration;
512    /// #
513    /// # async fn index(storage: Storage) -> Result<String, Error> {
514    /// storage.persist("key").await?;
515    /// #     Ok("deleted".to_string())
516    /// # }
517    /// ```
518    pub async fn persist(&self, key: impl AsRef<[u8]>) -> Result<()> {
519        self.store
520            .persist(self.scope.clone(), key.as_ref().into())
521            .await
522    }
523}
524
525/// Builder struct for [`Storage`](../struct.Storage.html)
526///
527/// A provider can either implement [`ExpiryStore`](trait.ExpiryStore.html) directly,
528/// or implement [`Store`](trait.Store.html) and rely on another provider to provide
529/// expiration capablities. The builder will polyfill a [`ExpiryStore`](trait.ExpiryStore.html)
530/// by combining an [`Expiry`](trait.Expiry.html) and a [`Store`](trait.Store.html) itself.
531///
532/// If there is no [`Expiry`](trait.Expiry.html) set in either of the ways, it will result in runtime
533/// errors when calling methods which require that functionality.
534#[derive(Default)]
535pub struct StorageBuilder {
536    store: Option<Arc<dyn Store>>,
537    expiry: Option<Arc<dyn Expiry>>,
538    expiry_store: Option<Arc<dyn ExpiryStore>>,
539    #[cfg(feature = "with-serde")]
540    format: Format,
541}
542
543impl StorageBuilder {
544    #[must_use = "Builder must be used by calling finish"]
545    /// This method can be used to set a [`Store`](trait.Store.html), the second call to this
546    /// method will overwrite the store.
547    pub fn store(mut self, store: impl Store + 'static) -> Self {
548        self.store = Some(Arc::new(store));
549        self
550    }
551
552    #[must_use = "Builder must be used by calling finish"]
553    /// This method can be used to set a [`Expiry`](trait.Expiry.html), the second call to this
554    /// method will overwrite the expiry.
555    ///
556    /// The expiry should work on the same storage as the provided store.
557    pub fn expiry(mut self, expiry: impl Expiry + 'static) -> Self {
558        self.expiry = Some(Arc::new(expiry));
559        self
560    }
561
562    #[must_use = "Builder must be used by calling finish"]
563    /// This method can be used to set an [`ExpiryStore`](trait.ExpiryStore.html) directly,
564    /// Its error to call [`expiry`](#method.expiry) or [`store`](#method.store) after calling this method.
565    pub fn expiry_store<T>(mut self, expiry_store: T) -> Self
566    where
567        T: 'static + Store + Expiry + ExpiryStore,
568    {
569        self.expiry_store = Some(Arc::new(expiry_store));
570        self
571    }
572
573    #[cfg(feature = "with-serde")]
574    #[must_use = "Builder must be used by calling finish"]
575    /// This method can be used to set the format storage will use for serialization/deserialization,
576    /// we will use default format if it is not called which can be None if there is no serde feature
577    /// enabled.
578    pub fn format(mut self, format: Format) -> Self {
579        self.format = format;
580        self
581    }
582
583    /// This method should be used after configuring the storage.
584    ///
585    /// ## Panics
586    /// If there is no store provided either by calling [`expiry_store`](#method.expiry_store)
587    /// or [`store`](#method.store) it will panic.
588    pub fn finish(self) -> Storage {
589        let expiry_store = if let Some(expiry_store) = self.expiry_store {
590            expiry_store
591        } else if let Some(store) = self.store {
592            Arc::new(self::private::ExpiryStoreGlue(store, self.expiry))
593        } else {
594            // It is a configuration error, so we just panic.
595            panic!("Storage builder needs at least a store");
596        };
597
598        Storage {
599            scope: Arc::new(GLOBAL_SCOPE),
600            store: expiry_store,
601            #[cfg(feature = "with-serde")]
602            format: self.format,
603        }
604    }
605}
606
607/// It is pretty much copied as-is from actix-web Data
608impl FromRequest for Storage {
609    type Error = actix_web::Error;
610    type Future = Ready<std::result::Result<Self, actix_web::Error>>;
611
612    #[inline]
613    fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
614        if let Some(st) = req.app_data::<Storage>() {
615            ready(Ok(st.clone()))
616        } else {
617            log::debug!(
618                "Failed to construct Storage(actix-storage). \
619                 Request path: {:?}",
620                req.path(),
621            );
622            ready(Err(ErrorInternalServerError(
623                "Storage is not configured, please refer to actix-storage documentation\
624                for more information.",
625            )))
626        }
627    }
628}
629
630mod private {
631    use std::sync::Arc;
632    use std::time::Duration;
633
634    use crate::{
635        error::{Result, StorageError},
636        provider::{Expiry, ExpiryStore, Store},
637    };
638
639    pub(crate) struct ExpiryStoreGlue(pub Arc<dyn Store>, pub Option<Arc<dyn Expiry>>);
640
641    #[async_trait::async_trait]
642    impl Expiry for ExpiryStoreGlue {
643        async fn expire(
644            &self,
645            scope: Arc<[u8]>,
646            key: Arc<[u8]>,
647            expire_in: Duration,
648        ) -> Result<()> {
649            if let Some(expiry) = self.1.clone() {
650                expiry.expire(scope, key, expire_in).await
651            } else {
652                Err(StorageError::MethodNotSupported)
653            }
654        }
655
656        async fn expiry(&self, scope: Arc<[u8]>, key: Arc<[u8]>) -> Result<Option<Duration>> {
657            if let Some(ref expiry) = self.1 {
658                expiry.expiry(scope, key).await
659            } else {
660                Err(StorageError::MethodNotSupported)
661            }
662        }
663
664        async fn extend(
665            &self,
666            scope: Arc<[u8]>,
667            key: Arc<[u8]>,
668            expire_in: Duration,
669        ) -> Result<()> {
670            if let Some(ref expiry) = self.1 {
671                expiry.extend(scope, key, expire_in).await
672            } else {
673                Err(StorageError::MethodNotSupported)
674            }
675        }
676
677        async fn persist(&self, scope: Arc<[u8]>, key: Arc<[u8]>) -> Result<()> {
678            if let Some(ref expiry) = self.1 {
679                expiry.persist(scope, key).await
680            } else {
681                Err(StorageError::MethodNotSupported)
682            }
683        }
684    }
685
686    #[async_trait::async_trait]
687    impl Store for ExpiryStoreGlue {
688        async fn set(&self, scope: Arc<[u8]>, key: Arc<[u8]>, value: Arc<[u8]>) -> Result<()> {
689            self.0.set(scope, key.clone(), value).await?;
690            if let Some(ref expiry) = self.1 {
691                expiry.set_called(key).await;
692            };
693            Ok(())
694        }
695
696        async fn get(&self, scope: Arc<[u8]>, key: Arc<[u8]>) -> Result<Option<Arc<[u8]>>> {
697            self.0.get(scope, key).await
698        }
699
700        async fn delete(&self, scope: Arc<[u8]>, key: Arc<[u8]>) -> Result<()> {
701            self.0.delete(scope, key).await
702        }
703
704        async fn contains_key(&self, scope: Arc<[u8]>, key: Arc<[u8]>) -> Result<bool> {
705            self.0.contains_key(scope, key).await
706        }
707    }
708
709    #[async_trait::async_trait]
710    impl ExpiryStore for ExpiryStoreGlue {
711        async fn set_expiring(
712            &self,
713            scope: Arc<[u8]>,
714            key: Arc<[u8]>,
715            value: Arc<[u8]>,
716            expire_in: Duration,
717        ) -> Result<()> {
718            if let Some(expiry) = self.1.clone() {
719                self.0.set(scope.clone(), key.clone(), value).await?;
720                expiry.expire(scope, key, expire_in).await
721            } else {
722                Err(StorageError::MethodNotSupported)
723            }
724        }
725
726        async fn get_expiring(
727            &self,
728            scope: Arc<[u8]>,
729            key: Arc<[u8]>,
730        ) -> Result<Option<(Arc<[u8]>, Option<Duration>)>> {
731            if let Some(expiry) = self.1.clone() {
732                let val = self.0.get(scope.clone(), key.clone()).await?;
733                if let Some(val) = val {
734                    let expiry = expiry.expiry(scope, key).await?;
735                    Ok(Some((val, expiry)))
736                } else {
737                    Ok(None)
738                }
739            } else {
740                Err(StorageError::MethodNotSupported)
741            }
742        }
743    }
744}
745
746#[cfg(test)]
747mod test {
748    use std::time::Duration;
749
750    use super::*;
751
752    #[actix::test]
753    async fn test_no_expiry() {
754        struct OnlyStore;
755
756        #[async_trait::async_trait]
757        impl Store for OnlyStore {
758            async fn set(&self, _: Arc<[u8]>, _: Arc<[u8]>, _: Arc<[u8]>) -> Result<()> {
759                Ok(())
760            }
761            async fn get(&self, _: Arc<[u8]>, _: Arc<[u8]>) -> Result<Option<Arc<[u8]>>> {
762                Ok(None)
763            }
764            async fn contains_key(&self, _: Arc<[u8]>, _: Arc<[u8]>) -> Result<bool> {
765                Ok(false)
766            }
767            async fn delete(&self, _: Arc<[u8]>, _: Arc<[u8]>) -> Result<()> {
768                Ok(())
769            }
770        }
771
772        let storage = Storage::build().store(OnlyStore).finish();
773
774        let k = "key";
775        let v = "value".as_bytes();
776        let d = Duration::from_secs(1);
777
778        // These checks should all result in error as we didn't set any expiry
779        assert!(storage.expire(k, d).await.is_err());
780        assert!(storage.expiry(k).await.is_err());
781        assert!(storage.extend(k, d).await.is_err());
782        assert!(storage.persist(k).await.is_err());
783        assert!(storage.set_expiring_bytes(k, v, d).await.is_err());
784        assert!(storage.get_expiring_bytes(k).await.is_err());
785
786        // These tests should all succeed
787        assert!(storage.set_bytes(k, v).await.is_ok());
788        assert!(storage.get_bytes(k).await.is_ok());
789        assert!(storage.delete(k).await.is_ok());
790        assert!(storage.contains_key(k).await.is_ok());
791    }
792
793    #[actix::test]
794    async fn test_expiry_store_polyfill() {
795        #[derive(Clone)]
796        struct SampleStore;
797
798        #[async_trait::async_trait]
799        impl Store for SampleStore {
800            async fn set(&self, _: Arc<[u8]>, _: Arc<[u8]>, _: Arc<[u8]>) -> Result<()> {
801                Ok(())
802            }
803            async fn get(&self, _: Arc<[u8]>, _: Arc<[u8]>) -> Result<Option<Arc<[u8]>>> {
804                Ok(Some("v".as_bytes().into()))
805            }
806            async fn contains_key(&self, _: Arc<[u8]>, _: Arc<[u8]>) -> Result<bool> {
807                Ok(false)
808            }
809            async fn delete(&self, _: Arc<[u8]>, _: Arc<[u8]>) -> Result<()> {
810                Ok(())
811            }
812        }
813
814        #[async_trait::async_trait]
815        impl Expiry for SampleStore {
816            async fn expire(&self, _: Arc<[u8]>, _: Arc<[u8]>, _: Duration) -> Result<()> {
817                Ok(())
818            }
819            async fn expiry(&self, _: Arc<[u8]>, _: Arc<[u8]>) -> Result<Option<Duration>> {
820                Ok(Some(Duration::from_secs(1)))
821            }
822            async fn extend(&self, _: Arc<[u8]>, _: Arc<[u8]>, _: Duration) -> Result<()> {
823                Ok(())
824            }
825            async fn persist(&self, _: Arc<[u8]>, _: Arc<[u8]>) -> Result<()> {
826                Ok(())
827            }
828        }
829
830        let k = "key";
831        let v = "value".as_bytes();
832        let d = Duration::from_secs(1);
833
834        let store = SampleStore;
835        let storage = Storage::build().store(store.clone()).expiry(store).finish();
836        assert!(storage
837            .set_expiring_bytes("key", "value", Duration::from_secs(1))
838            .await
839            .is_ok());
840
841        // These tests should all succeed
842        assert!(storage.expire(k, d).await.is_ok());
843        assert!(storage.expiry(k).await.is_ok());
844        assert!(storage.extend(k, d).await.is_ok());
845        assert!(storage.persist(k).await.is_ok());
846        assert!(storage.set_expiring_bytes(k, v, d).await.is_ok());
847        assert!(storage.get_expiring_bytes(k).await.is_ok());
848        assert!(storage.set_bytes(k, v).await.is_ok());
849        assert!(storage.get_bytes(k).await.is_ok());
850        assert!(storage.delete(k).await.is_ok());
851        assert!(storage.contains_key(k).await.is_ok());
852
853        // values should match
854        let res = storage.get_expiring_bytes("key").await;
855        assert!(res.is_ok());
856        assert!(res.unwrap() == Some(("v".as_bytes().into(), Some(Duration::from_secs(1)))));
857    }
858
859    #[test]
860    #[should_panic(expected = "Storage builder needs at least a store")]
861    fn test_no_sotre() {
862        Storage::build().finish();
863    }
864}