1use crate::core::{
2 entities::{
3 properties::prop::{check_for_unification, unify_types, PropError, PropType},
4 LayerId, LayerIds,
5 },
6 storage::{
7 arc_str::ArcStr,
8 dict_mapper::{DictMapper, LockedDictMapper, MaybeNew, PublicKeys, WriteLockedDictMapper},
9 },
10};
11use itertools::Either;
12use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
13use rustc_hash::FxHashMap;
14use serde::{Deserialize, Serialize};
15use std::{
16 fmt::{Debug, Formatter},
17 ops::{Deref, DerefMut},
18 sync::{
19 atomic::{self, AtomicUsize},
20 Arc,
21 },
22};
23
24pub const NODE_ID_PROP_KEY: &str = "_raphtory_node_id";
26pub const NODE_ID_IDX: usize = 0;
27
28pub const NODE_TYPE_PROP_KEY: &str = "_raphtory_node_type";
29pub const NODE_TYPE_IDX: usize = 1;
30
31pub const STATIC_GRAPH_LAYER_NAME: &str = "_static_graph";
32pub const STATIC_GRAPH_LAYER_ID: LayerId = LayerId(0);
33
34pub const STATIC_GRAPH_LAYER: LayerIds = LayerIds::One(STATIC_GRAPH_LAYER_ID);
35
36pub const DEFAULT_NODE_TYPE_ID: usize = 0;
38
39#[derive(Serialize, Deserialize, Debug, Default)]
40pub struct Meta {
41 temporal_prop_mapper: PropMapper,
42 metadata_mapper: PropMapper,
43 layer_mapper: DictMapper,
44 node_type_mapper: DictMapper,
45}
46
47impl Meta {
48 pub fn all_layer_iter(&self) -> impl Iterator<Item = (LayerId, ArcStr)> + use<'_> {
49 self.layer_mapper
50 .all_ids()
51 .map(LayerId)
52 .zip(self.layer_mapper.all_keys())
53 }
54
55 pub fn set_metadata_mapper(&mut self, meta: PropMapper) {
56 self.metadata_mapper = meta;
57 }
58
59 pub fn set_temporal_prop_mapper(&mut self, meta: PropMapper) {
60 self.temporal_prop_mapper = meta;
61 }
62
63 pub fn set_layer_mapper(&mut self, meta: DictMapper) {
64 self.layer_mapper = meta;
65 }
66 pub fn metadata_mapper(&self) -> &PropMapper {
67 &self.metadata_mapper
68 }
69
70 pub fn temporal_prop_mapper(&self) -> &PropMapper {
71 &self.temporal_prop_mapper
72 }
73
74 pub fn layer_meta(&self) -> &DictMapper {
75 &self.layer_mapper
76 }
77
78 pub fn node_type_meta(&self) -> &DictMapper {
79 &self.node_type_mapper
80 }
81
82 #[inline]
83 pub fn temporal_est_row_size(&self) -> usize {
84 self.temporal_prop_mapper.row_size()
85 }
86
87 #[inline]
88 pub fn const_est_row_size(&self) -> usize {
89 self.metadata_mapper.row_size()
90 }
91
92 pub fn new_for_nodes() -> Self {
93 let meta_layer = DictMapper::new_layer_mapper();
94 let meta_node_type = DictMapper::default();
95 meta_node_type.get_or_create_id("_default");
96
97 Self {
98 temporal_prop_mapper: PropMapper::default(),
99 metadata_mapper: PropMapper::new_with_private_fields(
100 [NODE_ID_PROP_KEY, NODE_TYPE_PROP_KEY],
101 [PropType::Empty, PropType::U64],
102 ),
103 layer_mapper: meta_layer,
104 node_type_mapper: meta_node_type, }
106 }
107
108 pub fn new_for_edges() -> Self {
109 let meta_layer = DictMapper::new_layer_mapper();
110 let meta_node_type = DictMapper::default();
111 meta_node_type.get_or_create_id("_default");
112
113 Self {
114 temporal_prop_mapper: PropMapper::default(),
115 metadata_mapper: PropMapper::default(),
116 layer_mapper: meta_layer,
117 node_type_mapper: meta_node_type, }
119 }
120
121 pub fn new_for_graph_props() -> Self {
122 let meta_layer = DictMapper::new_layer_mapper();
123 let meta_node_type = DictMapper::default();
124
125 Self {
127 temporal_prop_mapper: PropMapper::default(),
128 metadata_mapper: PropMapper::default(),
129 layer_mapper: meta_layer,
130 node_type_mapper: meta_node_type,
131 }
132 }
133
134 #[inline]
135 pub fn resolve_prop_id(
136 &self,
137 prop: &str,
138 dtype: PropType,
139 is_static: bool,
140 ) -> Result<MaybeNew<usize>, PropError> {
141 if is_static {
142 self.metadata_mapper.get_or_create_and_validate(prop, dtype)
143 } else {
144 self.temporal_prop_mapper
145 .get_or_create_and_validate(prop, dtype)
146 }
147 }
148
149 pub fn get_prop_id(&self, name: &str, is_static: bool) -> Option<usize> {
150 if is_static {
151 self.metadata_mapper.get_id(name)
152 } else {
153 self.temporal_prop_mapper.get_id(name)
154 }
155 }
156
157 pub fn get_prop_id_and_type(&self, name: &str, is_static: bool) -> Option<(usize, PropType)> {
158 if is_static {
159 self.metadata_mapper.get_id_and_dtype(name)
160 } else {
161 self.temporal_prop_mapper.get_id_and_dtype(name)
162 }
163 }
164
165 #[inline]
166 pub fn get_or_create_layer_id(&self, name: Option<&str>) -> MaybeNew<LayerId> {
167 self.layer_mapper
168 .get_or_create_id(name.unwrap_or("_default"))
169 .map(|l| LayerId(l))
170 }
171
172 #[inline]
173 pub fn get_default_node_type_id(&self) -> usize {
174 DEFAULT_NODE_TYPE_ID
175 }
176
177 #[inline]
178 pub fn get_or_create_node_type_id(&self, node_type: &str) -> MaybeNew<usize> {
179 self.node_type_mapper.get_or_create_id(node_type)
180 }
181
182 #[inline]
183 pub fn get_layer_id(&self, name: &str) -> Option<LayerId> {
184 self.layer_mapper.get_id(name).map(|l| LayerId(l))
185 }
186
187 #[inline]
188 pub fn get_default_layer_id(&self) -> Option<LayerId> {
189 self.layer_mapper.get_id("_default").map(|id| LayerId(id))
190 }
191
192 #[inline]
193 pub fn get_node_type_id(&self, node_type: &str) -> Option<usize> {
194 self.node_type_mapper.get_id(node_type)
195 }
196
197 pub fn get_layer_name_by_id(&self, id: LayerId) -> ArcStr {
198 self.layer_mapper.get_name(id.0)
199 }
200
201 pub fn get_node_type_name_by_id(&self, id: usize) -> Option<ArcStr> {
202 if id == DEFAULT_NODE_TYPE_ID {
203 None
204 } else {
205 Some(self.node_type_mapper.get_name(id))
206 }
207 }
208
209 pub fn get_all_node_types(&self) -> Vec<ArcStr> {
210 self.node_type_mapper
211 .keys()
212 .iter()
213 .filter_map(|key| {
214 if key != "_default" {
215 Some(key.clone())
216 } else {
217 None
218 }
219 })
220 .collect()
221 }
222
223 pub fn get_all_property_names(&self, is_static: bool) -> PublicKeys<ArcStr> {
224 if is_static {
225 self.metadata_mapper.keys()
226 } else {
227 self.temporal_prop_mapper.keys()
228 }
229 }
230
231 pub fn get_prop_name(&self, prop_id: usize, is_static: bool) -> ArcStr {
232 if is_static {
233 self.metadata_mapper.get_name(prop_id)
234 } else {
235 self.temporal_prop_mapper.get_name(prop_id)
236 }
237 }
238}
239
240#[derive(Default, Serialize, Deserialize)]
242pub struct PropMapper {
243 id_mapper: DictMapper,
245
246 dtypes: Arc<RwLock<Vec<PropType>>>,
248
249 row_size: AtomicUsize,
251}
252
253impl Debug for PropMapper {
254 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
255 f.write_str("{")?;
256 for (k, (id, dtype)) in self
257 .all_keys()
258 .iter()
259 .zip(self.all_ids().zip(self.d_types().iter()))
260 {
261 write!(f, "{k}: ({id}, {dtype:?}), ")?;
262 }
263 f.write_str("}")
264 }
265}
266
267impl Deref for PropMapper {
268 type Target = DictMapper;
269
270 #[inline]
271 fn deref(&self) -> &Self::Target {
272 &self.id_mapper
273 }
274}
275
276impl PropMapper {
277 pub fn new_with_private_fields(
278 fields: impl IntoIterator<Item = impl Into<ArcStr>>,
279 dtypes: impl IntoIterator<Item = PropType>,
280 ) -> Self {
281 let dtypes = Vec::from_iter(dtypes);
282 let row_size = dtypes.iter().map(|dtype| dtype.est_size()).sum();
283
284 PropMapper {
285 id_mapper: DictMapper::new_with_private_fields(fields),
286 row_size: AtomicUsize::new(row_size),
287 dtypes: Arc::new(RwLock::new(dtypes)),
288 }
289 }
290
291 pub fn d_types(&self) -> impl Deref<Target = Vec<PropType>> + '_ {
292 self.dtypes.read_recursive()
293 }
294
295 pub fn deep_clone(&self) -> Self {
296 let dtypes = self.dtypes.read_recursive().clone();
297 Self {
298 id_mapper: self.id_mapper.deep_clone(),
299 row_size: AtomicUsize::new(self.row_size.load(std::sync::atomic::Ordering::Relaxed)),
300 dtypes: Arc::new(RwLock::new(dtypes)),
301 }
302 }
303
304 #[inline]
305 pub fn row_size(&self) -> usize {
306 self.row_size.load(atomic::Ordering::Relaxed)
307 }
308
309 pub fn get_id_and_dtype(&self, prop: &str) -> Option<(usize, PropType)> {
310 self.get_id(prop).map(|id| {
311 let existing_dtype = self
312 .get_dtype(id)
313 .expect("Existing id should always have a dtype");
314 (id, existing_dtype)
315 })
316 }
317
318 pub fn get_or_create_and_validate(
319 &self,
320 prop: &str,
321 dtype: PropType,
322 ) -> Result<MaybeNew<usize>, PropError> {
323 let wrapped_id = self.id_mapper.get_or_create_id(prop);
324 let id = wrapped_id.inner();
325 let dtype_read = self.dtypes.read_recursive();
326
327 if let Some(old_type) = dtype_read.get(id) {
328 let mut unified = false;
329
330 if unify_types(&dtype, old_type, &mut unified).is_ok() {
331 if !unified {
332 return Ok(wrapped_id);
334 }
335 } else {
336 return Err(PropError {
337 name: prop.to_owned(),
338 expected: old_type.clone(),
339 actual: dtype,
340 });
341 }
342 }
343
344 drop(dtype_read);
347
348 let mut dtype_write = self.dtypes.write();
349
350 match dtype_write.get(id).cloned() {
351 Some(old_type) => {
352 let mut unified = false;
353
354 if let Ok(tpe) = unify_types(&dtype, &old_type, &mut unified) {
355 if unified {
356 let delta = tpe.est_size() - old_type.est_size();
359 self.row_size.fetch_add(delta, atomic::Ordering::Relaxed);
360 }
361
362 dtype_write[id] = tpe;
363 Ok(wrapped_id)
364 } else {
365 Err(PropError {
366 name: prop.to_owned(),
367 expected: old_type,
368 actual: dtype,
369 })
370 }
371 }
372 None => {
373 dtype_write.resize(id + 1, PropType::Empty);
375
376 self.row_size
377 .fetch_add(dtype.est_size(), atomic::Ordering::Relaxed);
378
379 dtype_write[id] = dtype;
380 Ok(wrapped_id)
381 }
382 }
383 }
384
385 pub fn set_id_and_dtype(&self, key: impl Into<ArcStr>, id: usize, dtype: PropType) {
386 self.set_id(key, id);
387 self.set_dtype(id, dtype);
388 }
389
390 pub fn set_dtype(&self, id: usize, dtype: PropType) {
391 let mut dtypes = self.dtypes.write();
392 if dtypes.len() <= id {
393 dtypes.resize(id + 1, PropType::Empty);
394 }
395 self.row_size
396 .fetch_add(dtype.est_size(), atomic::Ordering::Relaxed);
397 dtypes[id] = dtype;
398 }
399
400 pub fn get_dtype(&self, prop_id: usize) -> Option<PropType> {
401 self.dtypes.read_recursive().get(prop_id).cloned()
402 }
403
404 pub fn locked(&self) -> LockedPropMapper<'_> {
405 LockedPropMapper {
406 dict_mapper: self.id_mapper.read(),
407 d_types: self.dtypes.read_recursive(),
408 }
409 }
410
411 pub fn write_locked(&self) -> WriteLockedPropMapper<'_> {
412 WriteLockedPropMapper {
413 dict_mapper: self.id_mapper.write(),
414 d_types: self.dtypes.write(),
415 row_size: &self.row_size,
416 }
417 }
418}
419
420pub struct WriteLockedPropMapper<'a> {
422 dict_mapper: WriteLockedDictMapper<'a>,
424
425 d_types: RwLockWriteGuard<'a, Vec<PropType>>,
427
428 row_size: &'a AtomicUsize,
430}
431
432impl<'a> WriteLockedPropMapper<'a> {
433 pub fn new_id_and_dtype(&mut self, key: impl Into<ArcStr>, dtype: PropType) -> usize {
434 let id = self.dict_mapper.get_or_create_id(&key.into());
435 let dtypes = self.d_types.deref_mut();
436
437 if dtypes.len() <= id.inner() {
438 dtypes.resize(id.inner() + 1, PropType::Empty);
439 }
440
441 self.row_size
442 .fetch_add(dtype.est_size(), atomic::Ordering::Relaxed);
443
444 dtypes[id.inner()] = dtype;
445 id.inner()
446 }
447
448 pub fn set_id_and_dtype(&mut self, key: impl Into<ArcStr>, id: usize, dtype: PropType) {
449 self.dict_mapper.set_id(key, id);
450 self.set_dtype(id, dtype);
451 }
452
453 pub fn set_dtype(&mut self, id: usize, dtype: PropType) {
454 let dtypes = self.d_types.deref_mut();
455
456 if dtypes.len() <= id {
457 dtypes.resize(id + 1, PropType::Empty);
458 }
459
460 self.row_size
461 .fetch_add(dtype.est_size(), atomic::Ordering::Relaxed);
462
463 dtypes[id] = dtype;
464 }
465
466 pub fn set_or_unify_id_and_dtype(
467 &mut self,
468 key: impl Into<ArcStr>,
469 id: usize,
470 dtype: PropType,
471 ) -> Result<(), PropError> {
472 self.dict_mapper.set_id(key, id);
473 self.set_or_unify_dtype(id, dtype)
474 }
475
476 pub fn set_or_unify_dtype(&mut self, id: usize, dtype: PropType) -> Result<(), PropError> {
477 let dtypes = self.d_types.deref_mut();
478
479 match dtypes.get_mut(id) {
480 None => {
481 dtypes.resize(id + 1, PropType::Empty);
482
483 self.row_size
484 .fetch_add(dtype.est_size(), atomic::Ordering::Relaxed);
485
486 dtypes[id] = dtype;
487 }
488 Some(old_dtype) => {
489 let mut unified = false;
490 let unified_type = unify_types(&old_dtype, &dtype, &mut unified)?;
491
492 if unified {
493 let delta = unified_type.est_size() - old_dtype.est_size();
496 self.row_size.fetch_add(delta, atomic::Ordering::Relaxed);
497 }
498
499 *old_dtype = unified_type;
500 }
501 }
502
503 Ok(())
504 }
505
506 pub fn get_dtype(&'a self, prop_id: usize) -> Option<&'a PropType> {
507 self.d_types.get(prop_id)
508 }
509
510 pub fn fast_proptype_check(
517 &mut self,
518 prop: &str,
519 dtype: PropType,
520 ) -> Result<Option<Either<usize, usize>>, PropError> {
521 fast_proptype_check(self.dict_mapper.map(), &self.d_types, prop, dtype)
522 }
523}
524
525pub struct LockedPropMapper<'a> {
526 dict_mapper: LockedDictMapper<'a>,
527 d_types: RwLockReadGuard<'a, Vec<PropType>>,
528}
529
530impl<'a> LockedPropMapper<'a> {
531 pub fn get_id(&self, prop: &str) -> Option<usize> {
532 self.dict_mapper.get_id(prop)
533 }
534
535 pub fn get_dtype(&'a self, prop_id: usize) -> Option<&'a PropType> {
536 self.d_types.get(prop_id)
537 }
538
539 pub fn fast_proptype_check(
546 &self,
547 prop: &str,
548 dtype: PropType,
549 ) -> Result<Option<Either<usize, usize>>, PropError> {
550 fast_proptype_check(self.dict_mapper.map(), &self.d_types, prop, dtype)
551 }
552
553 pub fn iter_ids_and_types(&self) -> impl Iterator<Item = (usize, &ArcStr, &PropType)> {
554 self.dict_mapper
555 .iter_ids()
556 .map(move |(id, name)| (id, name, &self.d_types[id]))
557 }
558}
559
560fn fast_proptype_check(
561 mapper: &FxHashMap<ArcStr, usize>,
562 d_types: &[PropType],
563 prop: &str,
564 dtype: PropType,
565) -> Result<Option<Either<usize, usize>>, PropError> {
566 match mapper.get(prop) {
567 Some(&id) => {
568 let existing_dtype = d_types
569 .get(id)
570 .expect("Existing id should always have a dtype");
571
572 let fast_check = check_for_unification(&dtype, existing_dtype);
573 if fast_check.is_none() {
574 return Ok(Some(Either::Right(id)));
576 }
577 let can_unify = fast_check.unwrap();
578 if can_unify {
579 Ok(Some(Either::Left(id)))
580 } else {
581 Err(PropError {
582 name: prop.to_string(),
583 expected: existing_dtype.clone(),
584 actual: dtype,
585 })
586 }
587 }
588 None => Ok(None),
589 }
590}
591
592#[cfg(test)]
593mod prop_mapper_tests {
594 use super::*;
595
596 #[test]
597 fn get_or_create_and_validate_new_property() {
598 let prop_mapper = PropMapper::default();
599 let result = prop_mapper.get_or_create_and_validate("new_prop", PropType::U8);
600
601 assert!(result.is_ok());
602 assert_eq!(result.unwrap().inner(), 0);
603 assert_eq!(prop_mapper.get_dtype(0), Some(PropType::U8));
604 }
605
606 #[test]
607 fn get_or_create_and_validate_existing_property_same_type() {
608 let prop_mapper = PropMapper::default();
609
610 prop_mapper
611 .get_or_create_and_validate("existing_prop", PropType::U8)
612 .unwrap();
613
614 let result = prop_mapper.get_or_create_and_validate("existing_prop", PropType::U8);
615
616 assert!(result.is_ok());
617 assert_eq!(result.unwrap().inner(), 0);
618 assert_eq!(prop_mapper.get_dtype(0), Some(PropType::U8));
619 }
620
621 #[test]
622 fn get_or_create_and_validate_existing_property_different_type() {
623 let prop_mapper = PropMapper::default();
624
625 prop_mapper
626 .get_or_create_and_validate("existing_prop", PropType::U8)
627 .unwrap();
628
629 let result = prop_mapper.get_or_create_and_validate("existing_prop", PropType::U16);
630
631 assert!(result.is_err());
632
633 if let Err(PropError {
634 name,
635 expected,
636 actual,
637 }) = result
638 {
639 assert_eq!(name, "existing_prop");
640 assert_eq!(expected, PropType::U8);
641 assert_eq!(actual, PropType::U16);
642 } else {
643 panic!("Expected PropertyTypeError");
644 }
645 }
646
647 #[test]
648 fn get_or_create_and_validate_unify_types() {
649 let prop_mapper = PropMapper::default();
650
651 prop_mapper
652 .get_or_create_and_validate("prop", PropType::Empty)
653 .unwrap();
654
655 let result = prop_mapper.get_or_create_and_validate("prop", PropType::U8);
656
657 assert!(result.is_ok());
658 assert_eq!(result.unwrap().inner(), 0);
659 assert_eq!(prop_mapper.get_dtype(0), Some(PropType::U8));
660 }
661
662 #[test]
663 fn get_or_create_and_validate_resize_vector() {
664 let prop_mapper = PropMapper::default();
665
666 prop_mapper.set_id_and_dtype("existing_prop", 5, PropType::U8);
667
668 let result = prop_mapper.get_or_create_and_validate("new_prop", PropType::U16);
669
670 assert!(result.is_ok());
671 assert_eq!(result.unwrap().inner(), 6);
672 assert_eq!(prop_mapper.get_dtype(6), Some(PropType::U16));
673 }
674
675 #[test]
676 fn get_or_create_and_validate_two_independent_properties() {
677 let prop_mapper = PropMapper::default();
678 let result1 = prop_mapper.get_or_create_and_validate("prop1", PropType::U8);
679 let result2 = prop_mapper.get_or_create_and_validate("prop2", PropType::U16);
680
681 assert!(result1.is_ok());
682 assert!(result2.is_ok());
683 assert_eq!(result1.unwrap().inner(), 0);
684 assert_eq!(result2.unwrap().inner(), 1);
685 assert_eq!(prop_mapper.get_dtype(0), Some(PropType::U8));
686 assert_eq!(prop_mapper.get_dtype(1), Some(PropType::U16));
687 }
688
689 #[test]
690 fn unify_types_increases_row_size() {
691 let map_1 = PropType::map([("name", PropType::Str)]);
692 let map_2 = PropType::map([("location", PropType::Str)]);
693
694 let mut unified = false;
695 let expected_type = unify_types(&map_1, &map_2, &mut unified).unwrap();
696 let expected_delta = expected_type.est_size() - map_1.est_size();
697
698 assert!(unified);
699 assert!(expected_delta > 0, "should grow est_size on unify");
700
701 let prop_mapper = PropMapper::default();
702 prop_mapper
703 .get_or_create_and_validate("attrs", map_1.clone())
704 .unwrap();
705
706 let before = prop_mapper.row_size();
707
708 assert_eq!(before, map_1.est_size());
709
710 prop_mapper
711 .get_or_create_and_validate("attrs", map_2.clone())
712 .unwrap();
713
714 let after = prop_mapper.row_size();
715
716 assert_eq!(after, before + expected_delta);
717 assert_eq!(after, expected_type.est_size());
718 assert_eq!(prop_mapper.get_dtype(0), Some(expected_type));
719 }
720}
721
722#[cfg(test)]
723mod write_locked_prop_mapper_tests {
724 use super::*;
725
726 #[test]
727 fn new_id_and_dtype() {
728 let prop_mapper = PropMapper::default();
729
730 let id = {
731 let mut locked = prop_mapper.write_locked();
732 locked.new_id_and_dtype("new_prop", PropType::U8)
733 };
734
735 assert_eq!(id, 0);
736 assert_eq!(prop_mapper.get_dtype(0), Some(PropType::U8));
737 }
738
739 #[test]
740 fn set_or_unify_existing_property_same_type() {
741 let prop_mapper = PropMapper::default();
742
743 let id = {
744 let mut locked = prop_mapper.write_locked();
745 let id = locked.new_id_and_dtype("existing_prop", PropType::U8);
746 locked.set_or_unify_dtype(id, PropType::U8).unwrap();
747 id
748 };
749
750 assert_eq!(id, 0);
751 assert_eq!(prop_mapper.get_dtype(0), Some(PropType::U8));
752 }
753
754 #[test]
755 fn set_or_unify_existing_property_different_type() {
756 let prop_mapper = PropMapper::default();
757
758 let result = {
759 let mut locked = prop_mapper.write_locked();
760 let id = locked.new_id_and_dtype("existing_prop", PropType::U8);
761
762 locked.set_or_unify_dtype(id, PropType::U16)
763 };
764
765 assert!(result.is_err());
766
767 if let Err(PropError {
768 expected, actual, ..
769 }) = result
770 {
771 assert_eq!(expected, PropType::U8);
772 assert_eq!(actual, PropType::U16);
773 } else {
774 panic!("Expected PropError");
775 }
776 }
777
778 #[test]
779 fn set_or_unify_types() {
780 let prop_mapper = PropMapper::default();
781
782 let id = {
783 let mut locked = prop_mapper.write_locked();
784 let id = locked.new_id_and_dtype("prop", PropType::Empty);
785 locked.set_or_unify_dtype(id, PropType::U8).unwrap();
786 id
787 };
788
789 assert_eq!(id, 0);
790 assert_eq!(prop_mapper.get_dtype(0), Some(PropType::U8));
791 }
792
793 #[test]
794 fn new_id_and_dtype_resize_vector() {
795 let prop_mapper = PropMapper::default();
796
797 let id = {
798 let mut locked = prop_mapper.write_locked();
799 locked.set_id_and_dtype("existing_prop", 5, PropType::U8);
800 locked.new_id_and_dtype("new_prop", PropType::U16)
801 };
802
803 assert_eq!(id, 6);
804 assert_eq!(prop_mapper.get_dtype(6), Some(PropType::U16));
805 }
806
807 #[test]
808 fn new_id_and_dtype_two_independent_properties() {
809 let prop_mapper = PropMapper::default();
810
811 let (id1, id2) = {
812 let mut locked = prop_mapper.write_locked();
813 let id1 = locked.new_id_and_dtype("prop1", PropType::U8);
814 let id2 = locked.new_id_and_dtype("prop2", PropType::U16);
815
816 (id1, id2)
817 };
818
819 assert_eq!(id1, 0);
820 assert_eq!(id2, 1);
821 assert_eq!(prop_mapper.get_dtype(0), Some(PropType::U8));
822 assert_eq!(prop_mapper.get_dtype(1), Some(PropType::U16));
823 }
824
825 #[test]
826 fn unify_types_increases_row_size() {
827 let map_1 = PropType::map([("name", PropType::Str)]);
828 let map_2 = PropType::map([("location", PropType::Str)]);
829
830 let mut unified = false;
831 let expected_type = unify_types(&map_1, &map_2, &mut unified).unwrap();
832 let expected_delta = expected_type.est_size() - map_1.est_size();
833
834 assert!(unified);
835 assert!(expected_delta > 0, "should grow est_size on unify");
836
837 let prop_mapper = PropMapper::default();
838 let id = {
839 let mut locked = prop_mapper.write_locked();
840 locked.new_id_and_dtype("attrs", map_1.clone())
841 };
842
843 let before = prop_mapper.row_size();
844 assert_eq!(before, map_1.est_size());
845
846 {
847 let mut locked = prop_mapper.write_locked();
848 locked.set_or_unify_dtype(id, map_2.clone()).unwrap();
849 }
850
851 let after = prop_mapper.row_size();
852 assert_eq!(after, before + expected_delta);
853 assert_eq!(after, expected_type.est_size());
854 assert_eq!(prop_mapper.get_dtype(0), Some(expected_type));
855 }
856}