arts_n_crafts/infrastructure/database/
in_memory_database.rs1use 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}