Skip to main content

cljrs_value/collections/
transient_map.rs

1/*
2 * transient_map.rs -- implementation of transient maps.
3 * Copyright (C) 2026 Casey Marshall
4 *
5 * This file is licensed under the Eclipse Public License, Version 1,
6 * the same license as Clojure.
7 */
8
9use crate::hash::{hash_combine_ordered, hash_combine_unordered};
10use crate::{ClojureHash, PersistentHashMap, Value, ValueError, ValueResult};
11use std::sync::Mutex;
12
13#[derive(Debug)]
14pub struct TransientMap {
15    map: Mutex<PersistentHashMap>,
16    persisted: Mutex<bool>,
17}
18
19impl TransientMap {
20    pub fn new() -> TransientMap {
21        TransientMap {
22            map: Mutex::new(PersistentHashMap::empty()),
23            persisted: Mutex::new(false),
24        }
25    }
26
27    pub fn new_from_map(map: &PersistentHashMap) -> TransientMap {
28        TransientMap {
29            map: Mutex::new(map.clone()),
30            persisted: Mutex::new(false),
31        }
32    }
33
34    pub fn assoc(&self, key: Value, value: Value) -> ValueResult<()> {
35        if *self.persisted.lock().unwrap() {
36            return Err(ValueError::TransientAlreadyPersisted);
37        }
38        let mut map = self.map.lock().unwrap();
39        *map = map.assoc(key, value);
40        Ok(())
41    }
42
43    pub fn dissoc(&self, key: &Value) -> ValueResult<()> {
44        if *self.persisted.lock().unwrap() {
45            return Err(ValueError::TransientAlreadyPersisted);
46        }
47        let mut map = self.map.lock().unwrap();
48        *map = map.dissoc(key);
49        Ok(())
50    }
51
52    pub fn persistent(&self) -> ValueResult<PersistentHashMap> {
53        let map = self.map.lock().unwrap();
54        let mut persisted = self.persisted.lock().unwrap();
55        if *persisted {
56            return Err(ValueError::TransientAlreadyPersisted);
57        }
58        *persisted = true;
59        Ok(map.clone())
60    }
61
62    pub fn find(&self, key: &Value) -> Option<(Value, Value)> {
63        let map = self.map.lock().unwrap();
64        let value = map.get(key);
65        value.map(|v| (key.clone(), v.clone()))
66    }
67
68    pub fn get(&self, key: &Value) -> Option<Value> {
69        self.map.lock().unwrap().get(key).cloned()
70    }
71
72    pub fn contains_key(&self, key: &Value) -> bool {
73        self.map.lock().unwrap().contains_key(key)
74    }
75
76    pub fn count(&self) -> usize {
77        let map = self.map.lock().unwrap();
78        map.count()
79    }
80}
81
82impl Clone for TransientMap {
83    fn clone(&self) -> Self {
84        Self {
85            map: Mutex::new(self.map.lock().unwrap().clone()),
86            persisted: Mutex::new(*self.persisted.lock().unwrap()),
87        }
88    }
89}
90
91impl ClojureHash for TransientMap {
92    fn clojure_hash(&self) -> u32 {
93        let mut hash: u32 = 0;
94        for (k, v) in self.map.lock().unwrap().iter() {
95            hash = hash_combine_unordered(
96                hash,
97                hash_combine_ordered(k.clojure_hash(), v.clojure_hash()),
98            );
99        }
100        hash
101    }
102}
103
104impl cljrs_gc::Trace for TransientMap {
105    fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
106        {
107            let map = self.map.lock().unwrap();
108            for (k, v) in map.iter() {
109                k.trace(visitor);
110                v.trace(visitor);
111            }
112        }
113    }
114
115    fn gc_size_extra(&self) -> usize {
116        let map = self.map.lock().unwrap();
117        map.gc_size_extra()
118    }
119}
120
121impl Default for TransientMap {
122    fn default() -> Self {
123        Self::new()
124    }
125}