Skip to main content

amethystate_core/primitives/
map_ops.rs

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}