Skip to main content

arts_n_crafts/infrastructure/database/
in_memory_database.rs

1use serde::Serialize;
2use std::collections::HashMap;
3use std::sync::Arc;
4use tokio::sync::Mutex;
5
6#[derive(Debug, PartialEq)]
7pub enum DatabaseError {
8    Unreachable,
9}
10
11pub struct InMemoryDatabase<T>
12where
13    T: Serialize + Send + Sync + Clone,
14{
15    data: Arc<Mutex<HashMap<String, Vec<T>>>>,
16    is_offline: bool,
17}
18
19impl<T> InMemoryDatabase<T>
20where
21    T: Serialize + Send + Sync + Clone,
22{
23    pub fn new() -> Self {
24        InMemoryDatabase {
25            data: Arc::new(Mutex::new(HashMap::new())),
26            is_offline: false,
27        }
28    }
29
30    pub fn go_offline(&mut self) {
31        self.is_offline = true
32    }
33
34    pub async fn store(&self, key: &str, value: T) -> Result<(), DatabaseError> {
35        if self.is_offline {
36            return Err(DatabaseError::Unreachable);
37        }
38        let mut data = self.data.lock().await;
39        data.entry(key.to_owned())
40            .or_insert_with(Vec::new)
41            .push(value.to_owned());
42
43        Ok(())
44    }
45
46    pub async fn query(&self, table_name: &str) -> Result<Vec<T>, DatabaseError> {
47        if self.is_offline {
48            return Err(DatabaseError::Unreachable);
49        }
50
51        let data = self.data.lock().await;
52        let result = data.get(table_name).cloned().unwrap_or_default();
53        Ok(result)
54    }
55}
56
57impl<T> Default for InMemoryDatabase<T>
58where
59    T: Serialize + Send + Sync + Clone,
60{
61    fn default() -> Self {
62        Self::new()
63    }
64}
65
66#[cfg(test)]
67mod in_memory_database_tests {
68    use super::*;
69    use rstest::{fixture, rstest};
70    const TABLE_NAME: &str = "users";
71
72    #[derive(Serialize, Clone, Debug, PartialEq)]
73    struct User {
74        name: String,
75    }
76
77    #[fixture]
78    fn user() -> Vec<User> {
79        vec![User {
80            name: "John".to_string(),
81        }]
82    }
83
84    #[rstest]
85    #[tokio::test]
86    async fn should_store_the_data(user: Vec<User>) {
87        let db = InMemoryDatabase::default();
88        let result = db.store(TABLE_NAME, user[0].to_owned()).await;
89        assert!(result.is_ok());
90    }
91
92    #[rstest]
93    #[tokio::test]
94    async fn should_fail_storing_if_database_is_offline(user: Vec<User>) {
95        let mut db = InMemoryDatabase::new();
96        db.go_offline();
97        let result = db.store(TABLE_NAME, user[0].to_owned()).await;
98        assert!(result.is_err());
99        assert_eq!(result.err().unwrap(), DatabaseError::Unreachable);
100    }
101
102    #[rstest]
103    #[tokio::test]
104    async fn should_query_the_data(user: Vec<User>) {
105        let db = InMemoryDatabase::new();
106        db.store(TABLE_NAME, user[0].to_owned())
107            .await
108            .expect("unexpected store failed");
109        let result = db.query(TABLE_NAME).await;
110        assert!(result.is_ok());
111    }
112
113    #[rstest]
114    #[tokio::test]
115    async fn should_fail_querying_if_database_is_offline() {
116        let mut db = InMemoryDatabase::<User>::new();
117        db.go_offline();
118        let result = db.query(TABLE_NAME).await;
119        assert!(result.is_err());
120        assert_eq!(result.err().unwrap(), DatabaseError::Unreachable);
121    }
122}