actix_storage_dashmap/
basic.rs

1use std::sync::Arc;
2
3use actix_storage::{dev::Store, Result};
4use dashmap::DashMap;
5
6type ScopeMap = DashMap<Arc<[u8]>, Arc<[u8]>>;
7type InternalMap = DashMap<Arc<[u8]>, ScopeMap>;
8
9/// A simple implementation of [`Store`](actix_storage::dev::Store) based on DashMap
10///
11/// This provider doesn't support key expiration thus Storage will return errors when trying to use methods
12/// that require expiration functionality if there is no expiry provided.
13///
14/// ## Example
15/// ```no_run
16/// use actix_storage::Storage;
17/// use actix_storage_dashmap::DashMapStore;
18/// use actix_web::{App, HttpServer};
19///
20/// #[actix_web::main]
21/// async fn main() -> std::io::Result<()> {
22///     let storage = Storage::build().store(DashMapStore::new()).finish();
23///     let server = HttpServer::new(move || {
24///         App::new()
25///             .data(storage.clone())
26///     });
27///     server.bind("localhost:5000")?.run().await
28/// }
29/// ```
30#[derive(Debug, Default)]
31pub struct DashMapStore {
32    map: InternalMap,
33}
34
35impl DashMapStore {
36    /// Make a new store, with default capacity of 0
37    pub fn new() -> Self {
38        Self::default()
39    }
40
41    /// Make a new store, with specified capacity
42    pub fn with_capacity(capacity: usize) -> Self {
43        Self {
44            map: DashMap::with_capacity(capacity),
45        }
46    }
47
48    /// Make a new store from a hashmap
49    pub fn from_dashmap(map: InternalMap) -> Self {
50        Self { map }
51    }
52}
53
54#[async_trait::async_trait]
55impl Store for DashMapStore {
56    async fn set(&self, scope: Arc<[u8]>, key: Arc<[u8]>, value: Arc<[u8]>) -> Result<()> {
57        self.map.entry(scope).or_default().insert(key, value);
58        Ok(())
59    }
60
61    async fn get(&self, scope: Arc<[u8]>, key: Arc<[u8]>) -> Result<Option<Arc<[u8]>>> {
62        let value = if let Some(scope_map) = self.map.get(&scope) {
63            scope_map.get(&key).map(|v| v.clone())
64        } else {
65            None
66        };
67        Ok(value)
68    }
69
70    async fn delete(&self, scope: Arc<[u8]>, key: Arc<[u8]>) -> Result<()> {
71        self.map
72            .get_mut(&scope)
73            .and_then(|scope_map| scope_map.remove(&key));
74        Ok(())
75    }
76
77    async fn contains_key(&self, scope: Arc<[u8]>, key: Arc<[u8]>) -> Result<bool> {
78        Ok(self
79            .map
80            .get(&scope)
81            .map(|scope_map| scope_map.contains_key(&key))
82            .unwrap_or(false))
83    }
84}
85
86#[cfg(test)]
87mod test {
88    use super::*;
89    use actix_storage::tests::*;
90
91    #[test]
92    fn test_dashmap_basic_store() {
93        test_store(Box::pin(async { DashMapStore::default() }));
94    }
95
96    #[test]
97    fn test_dashmap_basic_formats() {
98        impl Clone for DashMapStore {
99            fn clone(&self) -> Self {
100                Self {
101                    map: self.map.clone(),
102                }
103            }
104        }
105        test_all_formats(Box::pin(async { DashMapStore::default() }));
106    }
107}