actix_storage_hashmap/
basic.rs

1use std::collections::HashMap;
2use std::sync::{Arc, RwLock};
3
4use actix_storage::{dev::Store, Result, StorageError};
5use thiserror::Error;
6
7#[derive(Debug, Error)]
8#[error("A proccess obtaining the lock has failed while keeping it.")]
9pub struct PoisionLockStorageError;
10
11type ScopeMap = HashMap<Arc<[u8]>, Arc<[u8]>>;
12type InternalMap = HashMap<Arc<[u8]>, ScopeMap>;
13
14/// A simple implementation of [`Store`](actix_storage::dev::Store) based on RwLock wrapped HashMap
15///
16/// This provider doesn't support key expiration thus Storage will return errors when trying to use methods
17/// that require expiration functionality.  
18///
19/// ## Example
20/// ```no_run
21/// use actix_storage::Storage;
22/// use actix_storage_hashmap::HashMapStore;
23/// use actix_web::{App, HttpServer};
24///
25/// #[actix_web::main]
26/// async fn main() -> std::io::Result<()> {
27///     let storage = Storage::build().store(HashMapStore::new()).finish();
28///     let server = HttpServer::new(move || {
29///         App::new()
30///             .data(storage.clone())
31///     });
32///     server.bind("localhost:5000")?.run().await
33/// }
34/// ```
35#[derive(Debug, Default)]
36pub struct HashMapStore {
37    map: RwLock<InternalMap>,
38}
39
40impl HashMapStore {
41    /// Make a new store, with default capacity of 0
42    pub fn new() -> Self {
43        Self::default()
44    }
45
46    /// Make a new store, with specified capacity
47    pub fn with_capacity(capacity: usize) -> Self {
48        Self {
49            map: RwLock::new(HashMap::with_capacity(capacity)),
50        }
51    }
52
53    /// Make a new store from a hashmap
54    pub fn from_hashmap(map: InternalMap) -> Self {
55        Self {
56            map: RwLock::new(map),
57        }
58    }
59}
60
61#[async_trait::async_trait]
62impl Store for HashMapStore {
63    async fn set(&self, scope: Arc<[u8]>, key: Arc<[u8]>, value: Arc<[u8]>) -> Result<()> {
64        match self.map.write() {
65            Ok(mut h) => {
66                h.entry(scope).or_default().insert(key, value);
67                Ok(())
68            }
69            Err(_) => Err(StorageError::custom(PoisionLockStorageError)),
70        }
71    }
72
73    async fn get(&self, scope: Arc<[u8]>, key: Arc<[u8]>) -> Result<Option<Arc<[u8]>>> {
74        match self.map.read() {
75            Ok(h) => Ok(h
76                .get(&scope)
77                .and_then(|scope_map| scope_map.get(&key))
78                .cloned()),
79            Err(_) => Err(StorageError::custom(PoisionLockStorageError)),
80        }
81    }
82
83    async fn delete(&self, scope: Arc<[u8]>, key: Arc<[u8]>) -> Result<()> {
84        match self.map.write() {
85            Ok(mut h) => {
86                h.get_mut(&scope)
87                    .and_then(|scope_map| scope_map.remove(&key));
88                Ok(())
89            }
90            Err(_) => Err(StorageError::custom(PoisionLockStorageError)),
91        }
92    }
93
94    async fn contains_key(&self, scope: Arc<[u8]>, key: Arc<[u8]>) -> Result<bool> {
95        match self.map.read() {
96            Ok(h) => Ok(h
97                .get(&scope)
98                .map(|scope_map| scope_map.contains_key(&key))
99                .unwrap_or(false)),
100            Err(_) => Err(StorageError::custom(PoisionLockStorageError)),
101        }
102    }
103}
104
105#[cfg(test)]
106mod test {
107    use super::*;
108    use actix_storage::tests::*;
109
110    #[test]
111    fn test_hashmap_basic_store() {
112        test_store(Box::pin(async { HashMapStore::default() }));
113    }
114
115    #[test]
116    fn test_hashmap_basic_formats() {
117        impl Clone for HashMapStore {
118            fn clone(&self) -> Self {
119                Self::default()
120            }
121        }
122        test_all_formats(Box::pin(async { HashMapStore::default() }));
123    }
124}