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