cinderblock_core/data_layer/
in_memory.rs1use std::{
2 any::{Any, TypeId},
3 collections::HashMap,
4 sync::{Arc, LazyLock},
5};
6
7use tokio::sync::RwLock;
8
9use crate::{PerformRead, ReadAction, Resource, data_layer::DataLayer};
10
11type State =
12 LazyLock<Arc<RwLock<HashMap<TypeId, HashMap<String, Box<dyn Any + Send + Sync + 'static>>>>>>;
13
14static STATE: State = LazyLock::new(Arc::default);
15
16#[derive(Debug)]
17pub struct InMemoryDataLayer {}
18impl InMemoryDataLayer {
19 pub(crate) fn new() -> Self {
20 Self {}
21 }
22}
23
24impl<R: Resource + 'static> DataLayer<R> for InMemoryDataLayer {
25 async fn create(&self, resource: R) -> crate::Result<R> {
26 let state = STATE.clone();
27 let mut state = state.write().await;
28
29 let map = state.entry(TypeId::of::<R>()).or_default();
30 map.insert(
31 resource.primary_key().to_string(),
32 Box::new(resource.clone()),
33 );
34
35 Ok(resource)
36 }
37
38 async fn read(&self, primary_key: &R::PrimaryKey) -> crate::Result<R> {
39 let state = STATE.clone();
40 let state = state.read().await;
41
42 let key = primary_key.to_string();
43
44 state
45 .get(&TypeId::of::<R>())
46 .and_then(|map| map.get(&key))
47 .and_then(|boxed| boxed.downcast_ref::<R>())
48 .cloned()
49 .ok_or_else(|| format!("resource not found for primary key `{key}`").into())
50 }
51
52 async fn update(&self, resource: R) -> crate::Result<()> {
53 let state = STATE.clone();
54 let mut state = state.write().await;
55
56 let key = resource.primary_key().to_string();
57
58 let map = state
59 .get_mut(&TypeId::of::<R>())
60 .ok_or_else(|| format!("resource not found for primary key `{key}`"))?;
61
62 if !map.contains_key(&key) {
63 return Err(format!("resource not found for primary key `{key}`").into());
64 }
65
66 map.insert(key, Box::new(resource.clone()));
67
68 Ok(())
69 }
70
71 async fn destroy(&self, primary_key: &R::PrimaryKey) -> crate::Result<R> {
72 let state = STATE.clone();
73 let mut state = state.write().await;
74
75 let key = primary_key.to_string();
76
77 let map = state
78 .get_mut(&TypeId::of::<R>())
79 .ok_or_else(|| format!("resource not found for primary key `{key}`"))?;
80
81 let boxed = map
82 .remove(&key)
83 .ok_or_else(|| format!("resource not found for primary key `{key}`"))?;
84
85 boxed
86 .downcast::<R>()
87 .map(|r| *r)
88 .map_err(|_| "failed to downcast destroyed resource".into())
89 }
90}
91
92pub trait InMemoryReadAction: ReadAction {
95 fn filter(row: &Self::Output, args: &Self::Arguments) -> bool;
96}
97
98pub trait InMemoryPagedReadAction: ReadAction {
102 fn filter(row: &Self::Output, args: &Self::Arguments) -> bool;
103}
104
105pub trait InMemoryPerformRead: ReadAction {
113 fn execute(
114 all: impl Iterator<Item = Self::Output>,
115 args: &Self::Arguments,
116 ) -> Self::Response;
117}
118
119impl<R, A> PerformRead<A> for InMemoryDataLayer
122where
123 R: Resource + 'static,
124 A: ReadAction<Output = R> + InMemoryPerformRead + 'static,
125{
126 async fn read(&self, args: &A::Arguments) -> crate::Result<A::Response> {
127 let state = STATE.clone();
128 let state = state.read().await;
129
130 let type_map = state.get(&TypeId::of::<R>());
131 let all = type_map
132 .iter()
133 .flat_map(|map| map.values())
134 .filter_map(|boxed| boxed.downcast_ref::<R>())
135 .cloned();
136
137 Ok(A::execute(all, args))
138 }
139}