grafeo_common/types/
property_map.rs1use std::collections::BTreeMap;
10use std::fmt;
11
12use smallvec::SmallVec;
13
14use super::value::{PropertyKey, Value};
15
16const INLINE_CAP: usize = 4;
22
23#[derive(Clone, PartialEq)]
30pub struct PropertyMap {
31 entries: SmallVec<[(PropertyKey, Value); INLINE_CAP]>,
32}
33
34impl PropertyMap {
35 #[inline]
37 #[must_use]
38 pub fn new() -> Self {
39 Self {
40 entries: SmallVec::new(),
41 }
42 }
43
44 #[inline]
46 #[must_use]
47 pub fn with_capacity(cap: usize) -> Self {
48 Self {
49 entries: SmallVec::with_capacity(cap),
50 }
51 }
52
53 #[inline]
55 #[must_use]
56 pub fn len(&self) -> usize {
57 self.entries.len()
58 }
59
60 #[inline]
62 #[must_use]
63 pub fn is_empty(&self) -> bool {
64 self.entries.is_empty()
65 }
66
67 #[must_use]
69 pub fn get(&self, key: &PropertyKey) -> Option<&Value> {
70 self.entries.iter().find(|(k, _)| k == key).map(|(_, v)| v)
71 }
72
73 #[must_use]
75 pub fn contains_key(&self, key: &PropertyKey) -> bool {
76 self.entries.iter().any(|(k, _)| k == key)
77 }
78
79 pub fn insert(&mut self, key: PropertyKey, value: Value) -> Option<Value> {
83 for entry in &mut self.entries {
84 if entry.0 == key {
85 let old = std::mem::replace(&mut entry.1, value);
86 return Some(old);
87 }
88 }
89 self.entries.push((key, value));
90 None
91 }
92
93 pub fn remove(&mut self, key: &PropertyKey) -> Option<Value> {
95 if let Some(pos) = self.entries.iter().position(|(k, _)| k == key) {
96 Some(self.entries.swap_remove(pos).1)
97 } else {
98 None
99 }
100 }
101
102 pub fn iter(&self) -> impl Iterator<Item = (&PropertyKey, &Value)> {
104 self.entries.iter().map(|(k, v)| (k, v))
105 }
106
107 #[must_use]
109 pub fn to_btree_map(&self) -> BTreeMap<PropertyKey, Value> {
110 self.entries.iter().cloned().collect()
111 }
112}
113
114impl Default for PropertyMap {
115 #[inline]
116 fn default() -> Self {
117 Self::new()
118 }
119}
120
121impl fmt::Debug for PropertyMap {
122 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123 f.debug_map()
124 .entries(self.entries.iter().map(|(k, v)| (k, v)))
125 .finish()
126 }
127}
128
129impl FromIterator<(PropertyKey, Value)> for PropertyMap {
130 fn from_iter<I: IntoIterator<Item = (PropertyKey, Value)>>(iter: I) -> Self {
131 let iter = iter.into_iter();
132 let (lower, _) = iter.size_hint();
133 let mut map = Self::with_capacity(lower);
134 for (k, v) in iter {
135 map.insert(k, v);
136 }
137 map
138 }
139}
140
141impl IntoIterator for PropertyMap {
142 type Item = (PropertyKey, Value);
143 type IntoIter = smallvec::IntoIter<[(PropertyKey, Value); INLINE_CAP]>;
144
145 fn into_iter(self) -> Self::IntoIter {
146 self.entries.into_iter()
147 }
148}
149
150impl<'a> IntoIterator for &'a PropertyMap {
151 type Item = (&'a PropertyKey, &'a Value);
152 type IntoIter = std::iter::Map<
153 std::slice::Iter<'a, (PropertyKey, Value)>,
154 fn(&'a (PropertyKey, Value)) -> (&'a PropertyKey, &'a Value),
155 >;
156
157 fn into_iter(self) -> Self::IntoIter {
158 self.entries.iter().map(|(k, v)| (k, v))
159 }
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165
166 #[test]
167 fn insert_and_get() {
168 let mut map = PropertyMap::new();
169 map.insert(PropertyKey::new("name"), Value::from("Alice"));
170 map.insert(PropertyKey::new("age"), Value::from(30i64));
171
172 assert_eq!(map.len(), 2);
173 assert_eq!(
174 map.get(&PropertyKey::new("name")).and_then(Value::as_str),
175 Some("Alice")
176 );
177 assert_eq!(
178 map.get(&PropertyKey::new("age")).and_then(Value::as_int64),
179 Some(30)
180 );
181 }
182
183 #[test]
184 fn insert_replaces_existing() {
185 let mut map = PropertyMap::new();
186 map.insert(PropertyKey::new("x"), Value::from(1i64));
187 let old = map.insert(PropertyKey::new("x"), Value::from(2i64));
188 assert_eq!(old, Some(Value::from(1i64)));
189 assert_eq!(map.len(), 1);
190 assert_eq!(
191 map.get(&PropertyKey::new("x")).and_then(Value::as_int64),
192 Some(2)
193 );
194 }
195
196 #[test]
197 fn remove() {
198 let mut map = PropertyMap::new();
199 map.insert(PropertyKey::new("a"), Value::from(1i64));
200 map.insert(PropertyKey::new("b"), Value::from(2i64));
201
202 let removed = map.remove(&PropertyKey::new("a"));
203 assert_eq!(removed, Some(Value::from(1i64)));
204 assert_eq!(map.len(), 1);
205 assert!(map.get(&PropertyKey::new("a")).is_none());
206 assert!(map.get(&PropertyKey::new("b")).is_some());
207 }
208
209 #[test]
210 fn contains_key() {
211 let mut map = PropertyMap::new();
212 map.insert(PropertyKey::new("x"), Value::Null);
213 assert!(map.contains_key(&PropertyKey::new("x")));
214 assert!(!map.contains_key(&PropertyKey::new("y")));
215 }
216
217 #[test]
218 fn from_iterator() {
219 let pairs = vec![
220 (PropertyKey::new("a"), Value::from(1i64)),
221 (PropertyKey::new("b"), Value::from(2i64)),
222 ];
223 let map: PropertyMap = pairs.into_iter().collect();
224 assert_eq!(map.len(), 2);
225 }
226
227 #[test]
228 fn into_iterator() {
229 let mut map = PropertyMap::new();
230 map.insert(PropertyKey::new("x"), Value::from(1i64));
231 map.insert(PropertyKey::new("y"), Value::from(2i64));
232
233 let pairs: Vec<_> = map.into_iter().collect();
234 assert_eq!(pairs.len(), 2);
235 }
236
237 #[test]
238 fn empty() {
239 let map = PropertyMap::new();
240 assert!(map.is_empty());
241 assert_eq!(map.len(), 0);
242 assert!(map.get(&PropertyKey::new("x")).is_none());
243 }
244
245 #[test]
246 fn inline_capacity() {
247 let mut map = PropertyMap::new();
249 for i in 0..4 {
250 map.insert(PropertyKey::new(format!("k{i}")), Value::from(i as i64));
251 }
252 assert_eq!(map.len(), 4);
253 assert!(!map.entries.spilled());
254 }
255
256 #[test]
257 fn spills_to_heap_beyond_capacity() {
258 let mut map = PropertyMap::new();
259 for i in 0..10 {
260 map.insert(PropertyKey::new(format!("k{i}")), Value::from(i as i64));
261 }
262 assert_eq!(map.len(), 10);
263 assert!(map.entries.spilled());
264 for i in 0..10 {
266 assert!(map.contains_key(&PropertyKey::new(format!("k{i}"))));
267 }
268 }
269
270 #[test]
271 fn debug_format() {
272 let mut map = PropertyMap::new();
273 map.insert(PropertyKey::new("name"), Value::from("Alice"));
274 let dbg = format!("{map:?}");
275 assert!(dbg.contains("name"));
276 }
277
278 #[test]
279 fn from_empty_iterator() {
280 let map: PropertyMap = std::iter::empty().collect();
281 assert!(map.is_empty());
282 }
283
284 #[test]
285 fn ref_iterator() {
286 let mut map = PropertyMap::new();
287 map.insert(PropertyKey::new("a"), Value::from(1i64));
288 map.insert(PropertyKey::new("b"), Value::from(2i64));
289 let pairs: Vec<_> = (&map).into_iter().collect();
290 assert_eq!(pairs.len(), 2);
291 }
292
293 #[test]
294 fn default_is_empty() {
295 let map = PropertyMap::default();
296 assert!(map.is_empty());
297 }
298
299 #[test]
300 fn remove_nonexistent() {
301 let mut map = PropertyMap::new();
302 assert_eq!(map.remove(&PropertyKey::new("missing")), None);
303 }
304
305 #[test]
306 fn to_btree_map_round_trip() {
307 let mut map = PropertyMap::new();
308 map.insert(PropertyKey::new("x"), Value::from(1i64));
309 map.insert(PropertyKey::new("y"), Value::from(2i64));
310 let btree = map.to_btree_map();
311 assert_eq!(btree.len(), 2);
312 assert_eq!(btree[&PropertyKey::new("x")], Value::from(1i64));
313 }
314}