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}