1use crate::AmeBackendSync;
2use crate::primitives::error::{ReactiveMapError, ReactiveMapResult};
3use crate::primitives::map_core::{ReactiveMapKey, ReactiveMapValue};
4use crate::{MapChange, ReactiveMapCore};
5use std::borrow::Borrow;
6
7use serde::de::DeserializeOwned;
8use std::fmt::Display;
9use std::str::FromStr;
10use std::sync::Arc;
11use uuid::Uuid;
12
13pub fn map_get<B, K, V>(backend: &B, path: &str, key: &K) -> ReactiveMapResult<Option<V>, B::Error>
14where
15 B: AmeBackendSync,
16 K: Display,
17 V: DeserializeOwned,
18{
19 Ok(backend.get(&format!("{}.{}", path, key))?)
20}
21
22pub fn map_contains_key<B, K, V>(
23 backend: &B,
24 path: &str,
25 key: &K,
26) -> ReactiveMapResult<bool, B::Error>
27where
28 B: AmeBackendSync,
29 K: Display,
30 V: DeserializeOwned,
31{
32 map_get::<B, K, V>(backend, path, key).map(|v| v.is_some())
33}
34
35pub fn map_entries<B, K, V>(backend: &B, path: &str) -> ReactiveMapResult<Vec<(K, V)>, B::Error>
36where
37 B: AmeBackendSync,
38 K: FromStr,
39 V: DeserializeOwned + Default,
40{
41 let prefix = format!("{}.", path);
42 let kvs = backend.scan_prefix(&prefix)?;
43
44 let mut results = Vec::new();
45
46 for (full_path, raw) in kvs {
47 if let Some(key_str) = full_path.strip_prefix(&prefix)
48 && let Ok(k) = K::from_str(key_str)
49 && let Ok(v) = backend.decode::<V>(raw.borrow())
50 {
51 results.push((k, v));
52 }
53 }
54
55 Ok(results)
56}
57
58pub fn map_len<B>(backend: &B, path: &str) -> ReactiveMapResult<usize, B::Error>
59where
60 B: AmeBackendSync,
61{
62 Ok(backend
63 .scan_prefix(&format!("{}.", path))
64 .map(|kvs| kvs.len())?)
65}
66
67pub fn map_set_existing<B, K, V>(
68 backend: &B,
69 core: &ReactiveMapCore<K, V>,
70 path: Arc<str>,
71 key: K,
72 value: &V,
73 notify_after_commit: bool,
74 source: Option<Uuid>,
75) -> ReactiveMapResult<(), B::Error>
76where
77 B: AmeBackendSync,
78 K: ReactiveMapKey,
79 V: ReactiveMapValue,
80{
81 let full_path = format!("{}.{}", path, key);
82 let old_value = match backend.get::<V>(&full_path)? {
83 Some(old_value) => old_value,
84 None => return Err(ReactiveMapError::KeyNotFound(key.to_string())),
85 };
86
87 let change = MapChange::Update {
88 key,
89 old_value,
90 new_value: value.clone(),
91 source,
92 };
93
94 map_apply_change(backend, core, path, change, notify_after_commit)
95}
96
97pub fn map_set_or_create<B, K, V>(
98 backend: &B,
99 core: &ReactiveMapCore<K, V>,
100 path: Arc<str>,
101 key: K,
102 value: &V,
103 notify_after_commit: bool,
104 source: Option<Uuid>,
105) -> ReactiveMapResult<(), B::Error>
106where
107 B: AmeBackendSync,
108 K: ReactiveMapKey,
109 V: ReactiveMapValue,
110{
111 let full_path = format!("{}.{}", path, key);
112 let old_value = backend.get::<V>(&full_path)?;
113 let change = if let Some(old_value) = old_value {
114 MapChange::Update {
115 key,
116 old_value,
117 new_value: value.clone(),
118 source,
119 }
120 } else {
121 MapChange::Insert {
122 key,
123 value: value.clone(),
124 source,
125 }
126 };
127
128 map_apply_change(backend, core, path, change, notify_after_commit)
129}
130
131pub fn map_remove<B, K, V>(
132 backend: &B,
133 core: &ReactiveMapCore<K, V>,
134 path: Arc<str>,
135 key: K,
136 notify_after_commit: bool,
137 source: Option<Uuid>,
138) -> ReactiveMapResult<Option<V>, B::Error>
139where
140 B: AmeBackendSync,
141 K: ReactiveMapKey,
142 V: ReactiveMapValue,
143{
144 let exists = core.cache.lock().unwrap().contains_key(&key);
145 if !exists {
146 return Ok(None);
147 }
148
149 let full_path = format!("{}.{}", path, key);
150 let old_value = backend.get::<V>(&full_path)?;
151 if let Some(old_value) = old_value {
152 let change = MapChange::Remove {
153 key,
154 old_value: old_value.clone(),
155 source,
156 };
157 map_apply_change(backend, core, path, change, notify_after_commit)?;
158 Ok(Some(old_value))
159 } else {
160 core.cache.lock().unwrap().remove(&key);
161 Ok(None)
162 }
163}
164
165pub fn map_clear<B, K, V>(
166 backend: &B,
167 core: &ReactiveMapCore<K, V>,
168 path: Arc<str>,
169 notify_after_commit: bool,
170 source: Option<Uuid>,
171) -> ReactiveMapResult<(), B::Error>
172where
173 B: AmeBackendSync,
174 K: ReactiveMapKey,
175 V: ReactiveMapValue,
176{
177 map_apply_change(
178 backend,
179 core,
180 path,
181 MapChange::Clear { source },
182 notify_after_commit,
183 )
184}
185
186pub fn map_apply_change<B, K, V>(
187 backend: &B,
188 core: &ReactiveMapCore<K, V>,
189 path: Arc<str>,
190 change: MapChange<K, V>,
191 notify_after_commit: bool,
192) -> ReactiveMapResult<(), B::Error>
193where
194 B: AmeBackendSync,
195 K: ReactiveMapKey,
196 V: ReactiveMapValue,
197{
198 let context_path: Arc<str> = match change.key() {
199 Some(key) => format!("{}.{}", path, key).into(),
200 None => path.clone(),
201 };
202
203 let processed = core
204 .run_interceptors(context_path, change)
205 .map_err(|_| ReactiveMapError::Intercepted)?;
206
207 match &processed {
208 MapChange::Insert { key, value, .. }
209 | MapChange::Update {
210 key,
211 new_value: value,
212 ..
213 } => {
214 backend.set_with_source(&format!("{}.{}", path, key), value, processed.source())?;
215 }
216 MapChange::Remove { key, .. } => {
217 backend.delete_with_source(&format!("{}.{}", path, key), processed.source())?;
218 }
219 MapChange::Clear { .. } => {
220 let prefix = format!("{}.", path);
221 let kvs = backend.scan_prefix(&prefix)?;
222 for (full_path, _) in kvs {
223 backend.delete_with_source(&full_path, processed.source())?;
224 }
225 }
226 }
227
228 map_apply_remote_change(core, &processed);
229 if notify_after_commit {
230 core.notify(&processed);
231 }
232
233 Ok(())
234}
235
236pub fn map_apply_remote_change<K, V>(core: &ReactiveMapCore<K, V>, change: &MapChange<K, V>)
237where
238 K: ReactiveMapKey,
239 V: ReactiveMapValue,
240{
241 let mut keys = core.cache.lock().unwrap();
242 match change {
243 MapChange::Insert { key, value, .. }
244 | MapChange::Update {
245 key,
246 new_value: value,
247 ..
248 } => {
249 keys.insert(key.clone(), value.clone());
250 }
251 MapChange::Remove { key, .. } => {
252 keys.remove(key);
253 }
254 MapChange::Clear { .. } => {
255 keys.clear();
256 }
257 }
258}