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<rpds::HashTrieMapSync<Value, Value>>,
16    persisted: Mutex<bool>,
17}
18
19impl TransientMap {
20    pub fn new() -> TransientMap {
21        TransientMap {
22            map: Mutex::new(rpds::HashTrieMapSync::default()),
23            persisted: Mutex::new(false),
24        }
25    }
26
27    pub fn new_from_map(map: &rpds::HashTrieMapSync<Value, Value>) -> 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.insert_mut(key.clone(), value.clone());
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.remove_mut(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(PersistentHashMap::new(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| (v.clone(), v.clone()))
66    }
67
68    pub fn count(&self) -> usize {
69        let map = self.map.lock().unwrap();
70        map.size()
71    }
72}
73
74impl Clone for TransientMap {
75    fn clone(&self) -> Self {
76        Self {
77            map: Mutex::new(self.map.lock().unwrap().clone()),
78            persisted: Mutex::new(*self.persisted.lock().unwrap()),
79        }
80    }
81}
82
83impl ClojureHash for TransientMap {
84    fn clojure_hash(&self) -> u32 {
85        let mut hash: u32 = 0;
86        for (k, v) in self.map.lock().unwrap().iter() {
87            hash = hash_combine_unordered(
88                hash,
89                hash_combine_ordered(k.clojure_hash(), v.clojure_hash()),
90            );
91        }
92        hash
93    }
94}
95
96impl cljrs_gc::Trace for TransientMap {
97    fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
98        {
99            let map = self.map.lock().unwrap();
100            for (k, v) in map.iter() {
101                k.trace(visitor);
102                v.trace(visitor);
103            }
104        }
105    }
106}
107
108impl Default for TransientMap {
109    fn default() -> Self {
110        Self::new()
111    }
112}