1use std::borrow::Cow;
15use std::collections::{BTreeMap, BTreeSet};
16use std::fmt::Debug;
17
18use slotmap::{Key, new_key_type};
19use snafu::prelude::*;
20use uuid::Uuid;
21
22use crate::aggregate::Aggregate;
23use crate::category::Category;
24use crate::interner::{DenseSlottedBTreeInterner, Interner, ValueAlreadyExistsSnafu};
25use crate::metadata::{AnnotationValue, Field, Metadata, MetadataRef, WeightValue};
26use crate::transaction::{Transaction, TransactionMode};
27
28#[derive(Clone, Debug, Snafu)]
30#[snafu(visibility(pub))]
31pub enum Error {
32 InternerError { source: crate::interner::Error },
34}
35
36new_key_type! {
37 pub struct ObjectKey;
39
40 pub struct NameKey;
42
43 pub struct KindKey;
45
46 pub struct LabelKey;
48
49 pub struct WeightKey;
51
52 pub struct AnnotationKey;
54}
55
56pub type NameStore<ObjKey, Name> = Category<ObjKey, NameKey, NameKey, Name>;
58
59pub type KindStore<ObjKey, Kind> = Category<ObjKey, KindKey, KindKey, Kind>;
61
62pub type LabelStore<ObjKey, Label> = Category<ObjKey, BTreeSet<LabelKey>, LabelKey, Label>;
64
65pub type WeightStore<ObjKey, Weight, Value> =
67 Category<ObjKey, BTreeMap<WeightKey, Value>, WeightKey, Weight>;
68
69pub type AnnotationStore<ObjKey, Annotation, SubValue> =
71 Category<ObjKey, BTreeMap<AnnotationKey, SubValue>, AnnotationKey, Annotation>;
72
73pub type SimpleStore =
75 MetadataStore<ObjectKey, String, String, String, String, f64, String, serde_json::Value>;
76
77#[derive(Clone, Debug, bon::Builder)]
79#[cfg_attr(
80 feature = "serde",
81 derive(serde::Serialize, serde::Deserialize),
82 serde(default, rename_all = "camelCase")
83)]
84#[cfg_attr(feature = "reactive", derive(reactive_stores::Store))]
85pub struct MetadataStore<ObjKey, N, K, L, W, WV, A, AV>
86where
87 ObjKey: Key,
88 N: Field,
89 K: Field,
90 L: Field,
91 W: Field,
92 WV: WeightValue,
93 A: Field,
94 AV: AnnotationValue,
95{
96 ids: DenseSlottedBTreeInterner<ObjKey, Uuid>,
98
99 names: NameStore<ObjKey, N>,
101
102 kinds: KindStore<ObjKey, K>,
104
105 labels: LabelStore<ObjKey, L>,
107
108 weights: WeightStore<ObjKey, W, WV>,
110
111 annotations: AnnotationStore<ObjKey, A, AV>,
113}
114
115impl<ObjKey, N, K, L, W, WV, A, AV> Default for MetadataStore<ObjKey, N, K, L, W, WV, A, AV>
116where
117 ObjKey: Key,
118 N: Field,
119 K: Field,
120 L: Field,
121 W: Field,
122 WV: WeightValue,
123 A: Field,
124 AV: AnnotationValue,
125{
126 fn default() -> Self {
127 Self {
128 ids: Default::default(),
129 names: Default::default(),
130 kinds: Default::default(),
131 labels: Default::default(),
132 weights: Default::default(),
133 annotations: Default::default(),
134 }
135 }
136}
137
138impl<ObjKey, N, K, L, W, WV, A, AV> MetadataStore<ObjKey, N, K, L, W, WV, A, AV>
139where
140 ObjKey: Key,
141 N: Field,
142 K: Field,
143 L: Field,
144 W: Field,
145 WV: WeightValue,
146 A: Field,
147 AV: AnnotationValue,
148{
149 pub fn new() -> Self {
151 Self::default()
152 }
153
154 pub fn len(&self) -> usize {
156 self.ids.len()
157 }
158
159 pub fn is_empty(&self) -> bool {
161 self.len() == 0
162 }
163
164 pub fn add(&mut self) -> (ObjKey, Uuid) {
167 let id = Uuid::new_v4();
168 (self.intern_id(Cow::Borrowed(&id)), id)
169 }
170
171 pub fn intern_id(&mut self, id: Cow<'_, Uuid>) -> ObjKey {
173 self.ids.intern(id)
174 }
175
176 pub fn try_insert(&mut self, meta: Metadata<N, K, L, W, WV, A, AV>) -> Result<ObjKey, Error> {
178 let obj = self
179 .ids
180 .try_intern(Cow::Borrowed(&meta.id))
181 .context(InternerSnafu)?;
182
183 self.insert(meta);
184
185 Ok(obj)
186 }
187
188 pub fn insert(&mut self, meta: Metadata<N, K, L, W, WV, A, AV>) -> ObjKey {
190 let id = self.ids.intern_borrowed(&meta.id);
191
192 let mut tx = meta.as_replace_transaction();
193 tx.strip_id();
194
195 self.apply(id, tx);
196
197 id
198 }
199
200 pub fn try_apply<Id: AsRef<Uuid>>(
204 &mut self,
205 id: Id,
206 tx: Transaction<N, K, L, W, WV, A, AV>,
207 ) -> Result<(), Error> {
208 let obj = self.ids.try_get_key(id.as_ref()).context(InternerSnafu)?;
209
210 if let Some(new_id) = tx.id.as_ref()
211 && self.ids.get_key(new_id).as_ref() != Some(&obj)
212 {
213 ValueAlreadyExistsSnafu.fail().context(InternerSnafu)?;
214 }
215
216 self.apply(obj, tx);
217
218 Ok(())
219 }
220
221 pub fn apply(&mut self, obj: ObjKey, tx: Transaction<N, K, L, W, WV, A, AV>) {
224 let Transaction {
225 mode,
226 id: new_id,
227 name,
228 kind,
229 labels,
230 weights,
231 annotations,
232 } = tx;
233
234 if let Some(new_id) = new_id
235 && self.ids.get_key(&new_id).as_ref() != Some(&obj)
236 {
237 self.ids.remove_key(obj);
238 self.ids.intern_owned(new_id);
239 }
240
241 if name.is_some() {
243 self.names
244 .set_value_name_of_object(obj, name.map(Cow::Owned));
245 }
246
247 if kind.is_some() {
249 self.kinds
250 .set_value_name_of_object(obj, kind.map(Cow::Owned));
251 }
252
253 if let Some(labels) = labels {
254 if matches!(mode, TransactionMode::Replace) {
255 self.labels.remove_object(obj);
256 }
257 labels.into_iter().for_each(|label| {
258 self.labels
259 .insert_field_name_for_object(obj, Cow::Owned(label));
260 });
261 }
262
263 if let Some(weights) = weights {
264 if matches!(mode, TransactionMode::Replace) {
265 self.weights.remove_object(obj);
266 }
267 weights.into_iter().for_each(|(k, v)| {
268 self.weights
269 .insert_field_name_value_for_object(obj, Cow::Owned(k), v);
270 });
271 }
272
273 if let Some(annotations) = annotations {
274 if matches!(mode, TransactionMode::Replace) {
275 self.annotations.remove_object(obj);
276 }
277 annotations.into_iter().for_each(|(k, v)| {
278 self.annotations
279 .insert_field_name_value_for_object(obj, Cow::Owned(k), v);
280 });
281 };
282 }
283
284 pub fn try_apply_keyed(
288 &mut self,
289 obj: ObjKey,
290 tx: Transaction<NameKey, KindKey, LabelKey, WeightKey, WV, AnnotationKey, AV>,
291 ) -> Result<(), Error> {
292 if let Some(new_id) = tx.id.as_ref()
293 && self.ids.get_key(new_id).as_ref() != Some(&obj)
294 {
295 ValueAlreadyExistsSnafu.fail().context(InternerSnafu)?;
296 }
297
298 self.apply_keyed(obj, tx);
299
300 Ok(())
301 }
302
303 pub fn apply_keyed(
306 &mut self,
307 obj: ObjKey,
308 tx: Transaction<NameKey, KindKey, LabelKey, WeightKey, WV, AnnotationKey, AV>,
309 ) {
310 let Transaction {
311 mode,
312 id: new_id,
313 name,
314 kind,
315 labels,
316 weights,
317 annotations,
318 } = tx;
319
320 if let Some(new_id) = new_id
321 && self.ids.get_key(&new_id).as_ref() != Some(&obj)
322 {
323 self.ids.remove_key(obj);
324 self.ids.intern_owned(new_id);
325 }
326
327 if let Some(name) = name {
329 self.set_object_name_key(obj, name);
330 }
331
332 if let Some(kind) = kind {
334 self.set_object_kind_key(obj, kind);
335 }
336
337 if let Some(labels) = labels {
338 if matches!(mode, TransactionMode::Replace) {
339 self.labels.remove_object(obj);
340 }
341 labels.into_iter().for_each(|label| {
342 self.set_object_label_key(obj, label);
343 });
344 }
345
346 if let Some(weights) = weights {
347 if matches!(mode, TransactionMode::Replace) {
348 self.weights.remove_object(obj);
349 }
350 weights.into_iter().for_each(|(k, v)| {
351 self.set_object_weight_key(obj, k, v);
352 });
353 }
354
355 if let Some(annotations) = annotations {
356 if matches!(mode, TransactionMode::Replace) {
357 self.annotations.remove_object(obj);
358 }
359 annotations.into_iter().for_each(|(k, v)| {
360 self.set_object_annotation_key(obj, k, v);
361 });
362 };
363 }
364
365 pub fn intern_name(&mut self, name: Cow<'_, N>) -> NameKey {
367 self.names.field_key(name)
368 }
369
370 pub fn forget_name(&mut self, name: &N) -> Option<NameKey> {
372 self.names.remove_field_name(name)
373 }
374
375 pub fn forget_name_key(&mut self, name: NameKey) -> Option<N> {
377 self.names.remove_field(name)
378 }
379
380 pub fn set_object_name<Id: AsRef<Uuid>>(&mut self, id: Id, name: &N) {
382 let obj = self.intern_id(Cow::Borrowed(id.as_ref()));
383 let value = self.intern_name(Cow::Borrowed(name));
384 self.set_object_name_key(obj, value);
385 }
386
387 pub fn set_object_name_key(&mut self, obj: ObjKey, name: NameKey) {
389 self.names.insert_object_value(obj, name);
390 }
391
392 pub fn remove_object_name<Id: AsRef<Uuid>>(&mut self, id: Id) -> Option<&N> {
394 let obj = self.ids.get_key(id.as_ref())?;
395 self.remove_object_name_key(obj)
396 .and_then(|key| self.names.field_name(key))
397 }
398
399 pub fn remove_object_name_key(&mut self, obj: ObjKey) -> Option<NameKey> {
401 self.names.remove_object(obj)
402 }
403
404 pub fn intern_kind(&mut self, kind: Cow<K>) -> KindKey {
406 self.kinds.field_key(kind)
407 }
408
409 pub fn forget_kind(&mut self, kind: &K) -> Option<KindKey> {
411 self.kinds.remove_field_name(kind)
412 }
413
414 pub fn forget_kind_key(&mut self, kind: KindKey) -> Option<K> {
416 self.kinds.remove_field(kind)
417 }
418
419 pub fn set_object_kind<Id: AsRef<Uuid>>(&mut self, id: Id, kind: &K) {
421 let obj = self.intern_id(Cow::Borrowed(id.as_ref()));
422 let value = self.intern_kind(Cow::Borrowed(kind));
423 self.set_object_kind_key(obj, value);
424 }
425
426 pub fn set_object_kind_key(&mut self, obj: ObjKey, kind: KindKey) {
428 self.kinds.insert_object_value(obj, kind);
429 }
430
431 pub fn remove_object_kind<Id: AsRef<Uuid>>(&mut self, id: Id) -> Option<&K> {
433 let obj = self.ids.get_key(id.as_ref())?;
434 self.remove_object_kind_key(obj)
435 .and_then(|key| self.kinds.field_name(key))
436 }
437
438 pub fn remove_object_kind_key(&mut self, obj: ObjKey) -> Option<KindKey> {
440 self.kinds.remove_object(obj)
441 }
442
443 pub fn intern_label(&mut self, label: Cow<L>) -> LabelKey {
445 self.labels.field_key(label)
446 }
447
448 pub fn forget_label(&mut self, label: &L) -> Option<LabelKey> {
450 self.labels.remove_field_name(label)
451 }
452
453 pub fn forget_label_key(&mut self, label: LabelKey) -> Option<L> {
455 self.labels.remove_field(label)
456 }
457
458 pub fn set_object_label<Id: AsRef<Uuid>>(&mut self, id: Id, label: &L) {
460 let obj = self.intern_id(Cow::Borrowed(id.as_ref()));
461 self.labels
462 .insert_field_name_for_object(obj, Cow::Borrowed(label));
463 }
464
465 pub fn set_object_label_key(&mut self, obj: ObjKey, label: LabelKey) {
467 self.labels.insert_field_for_object(obj, label);
468 }
469
470 pub fn remove_object_label<Id: AsRef<Uuid>>(&mut self, id: Id, label: &L) {
472 if let Some(obj) = self.ids.get_key(id.as_ref()) {
473 self.labels.remove_field_name_from_object(obj, label);
474 }
475 }
476
477 pub fn strip_label_key(&mut self, obj: ObjKey, label: LabelKey) {
479 self.labels.remove_field_from_object(obj, label);
480 }
481
482 pub fn intern_weight(&mut self, weight: Cow<W>) -> WeightKey {
484 self.weights.field_key(weight)
485 }
486
487 pub fn forget_weight(&mut self, weight: &W) -> Option<WeightKey> {
490 self.weights.remove_field_name_values(weight)
491 }
492
493 pub fn forget_weight_key(&mut self, weight: WeightKey) -> Option<W> {
496 self.weights.remove_field_values(weight)
497 }
498
499 pub fn set_object_weight<Id: AsRef<Uuid>>(
502 &mut self,
503 id: Id,
504 weight: &W,
505 value: WV,
506 ) -> Option<WV> {
507 let obj = self.intern_id(Cow::Borrowed(id.as_ref()));
508 self.weights
509 .insert_field_name_value_for_object(obj, Cow::Borrowed(weight), value)
510 }
511
512 pub fn set_object_weight_key(&mut self, obj: ObjKey, key: WeightKey, value: WV) -> Option<WV> {
515 self.weights.insert_field_value_for_object(obj, key, value)
516 }
517
518 pub fn remove_object_weight<Id: AsRef<Uuid>>(&mut self, id: Id, weight: &W) -> Option<WV> {
520 self.ids.get_key(id.as_ref()).and_then(|obj| {
521 self.weights
522 .remove_field_name_value_from_object(obj, weight)
523 })
524 }
525
526 pub fn remove_object_weight_key(&mut self, obj: ObjKey, weight: WeightKey) -> Option<WV> {
528 self.weights.remove_field_value_from_object(obj, weight)
529 }
530
531 pub fn intern_annotation(&mut self, annotation: Cow<A>) -> AnnotationKey {
533 self.annotations.field_key(annotation)
534 }
535
536 pub fn forget_annotation(&mut self, annotation: &A) -> Option<AnnotationKey> {
539 self.annotations.remove_field_name_values(annotation)
540 }
541
542 pub fn forget_annotation_key(&mut self, key: AnnotationKey) -> Option<A> {
545 self.annotations.remove_field_values(key)
546 }
547
548 pub fn set_object_annotation<Id: AsRef<Uuid>>(
551 &mut self,
552 id: Id,
553 annotation: &A,
554 value: AV,
555 ) -> Option<AV> {
556 let obj = self.intern_id(Cow::Borrowed(id.as_ref()));
557 self.annotations
558 .insert_field_name_value_for_object(obj, Cow::Borrowed(annotation), value)
559 }
560
561 pub fn set_object_annotation_key(
564 &mut self,
565 obj: ObjKey,
566 key: AnnotationKey,
567 value: AV,
568 ) -> Option<AV> {
569 self.annotations
570 .insert_field_value_for_object(obj, key, value)
571 }
572
573 pub fn remove_object_annotation<Id: AsRef<Uuid>>(
575 &mut self,
576 id: Id,
577 annotation: &A,
578 ) -> Option<AV> {
579 self.ids.get_key(id.as_ref()).and_then(|obj| {
580 self.annotations
581 .remove_field_name_value_from_object(obj, annotation)
582 })
583 }
584
585 pub fn remove_object_annotation_key(
587 &mut self,
588 obj: ObjKey,
589 annotation: AnnotationKey,
590 ) -> Option<AV> {
591 self.annotations
592 .remove_field_value_from_object(obj, annotation)
593 }
594
595 pub fn remove_object<Id: AsRef<Uuid>>(&mut self, id: Id) -> bool {
598 match self.ids.remove_value(id.as_ref()) {
599 Some(obj) => self.remove_object_key(obj),
600 None => false,
601 }
602 }
603
604 pub fn remove_object_key(&mut self, obj: ObjKey) -> bool {
606 self.ids.remove_key(obj);
607 let did_name = self.names.remove_object(obj).is_some();
608 let did_kind = self.kinds.remove_object(obj).is_some();
609 let did_labels = self.labels.remove_object(obj).is_some();
610 let did_weights = self.weights.remove_object(obj).is_some();
611 let did_annotations = self.annotations.remove_object(obj).is_some();
612 did_name | did_kind | did_labels | did_weights | did_annotations
613 }
614
615 pub fn clean(&mut self) {
617 self.names.clean();
618 self.kinds.clean();
619 self.labels.clean();
620 self.weights.clean();
621 self.annotations.clean();
622 }
623
624 pub fn object_meta_named<'a, Id: AsRef<Uuid>>(
626 &'a self,
627 id: Id,
628 ) -> Option<MetadataRef<'a, N, K, L, W, WV, A, AV>> {
629 let id = *id.as_ref();
630 self.ids.get_key(&id).map(|obj| {
631 MetadataRef::builder()
632 .id(id)
633 .maybe_name(self.names.value_name_of_object(obj))
634 .maybe_kind(self.kinds.value_name_of_object(obj))
635 .labels(self.labels.named_value_of_object(obj))
636 .weights(self.weights.named_value_of_object(obj))
637 .annotations(self.annotations.named_value_of_object(obj))
638 .build()
639 })
640 }
641
642 pub fn object_meta_keyed(
644 &self,
645 obj: ObjKey,
646 ) -> Option<Metadata<NameKey, KindKey, LabelKey, WeightKey, WV, AnnotationKey, AV>> {
647 self.object_id(obj).map(|id| {
648 Metadata::builder()
649 .id(id)
650 .maybe_name(self.object_name_key(obj))
651 .maybe_kind(self.object_kind_key(obj))
652 .maybe_labels(self.object_label_keys(obj).cloned())
653 .maybe_weights(self.object_weights_keyed(obj).cloned())
654 .maybe_annotations(self.object_annotations_keyed(obj).cloned())
655 .build()
656 })
657 }
658
659 pub fn object_keys(&self) -> impl Iterator<Item = ObjKey> {
661 self.ids.keys()
662 }
663
664 pub fn object_ids(&self) -> impl Iterator<Item = &Uuid> {
666 self.ids.values()
667 }
668
669 pub fn object_key_id_pairs(&self) -> impl Iterator<Item = (ObjKey, &Uuid)> {
671 self.ids.items()
672 }
673
674 pub fn object_id(&self, obj: ObjKey) -> Option<Uuid> {
676 self.ids.get_value(obj).copied()
677 }
678
679 pub fn object_key<Id: AsRef<Uuid>>(&self, id: Id) -> Option<ObjKey> {
681 self.ids.get_key(id.as_ref())
682 }
683
684 pub fn name_store(&self) -> &NameStore<ObjKey, N> {
686 &self.names
687 }
688
689 pub fn name_field_names(&self) -> impl Iterator<Item = &N> {
691 self.names.field_names()
692 }
693
694 pub fn name_field_keys(&self) -> impl Iterator<Item = NameKey> {
696 self.names.field_keys()
697 }
698
699 pub fn name_field_items(&self) -> impl Iterator<Item = (NameKey, &N)> {
701 self.names.field_items()
702 }
703
704 pub fn objects_with_name(&self, name: &N) -> impl Iterator<Item = Uuid> {
706 self.names
707 .objects_with_field_name(name)
708 .into_iter()
709 .flatten()
710 .flat_map(|&obj| self.object_id(obj))
711 }
712
713 pub fn objects_with_name_key(&self, name: NameKey) -> impl Iterator<Item = ObjKey> {
715 self.names
716 .objects_with_field(name)
717 .into_iter()
718 .flatten()
719 .copied()
720 }
721
722 pub fn object_name<Id: AsRef<Uuid>>(&self, id: Id) -> Option<&N> {
724 self.object_key(id)
725 .and_then(|obj| self.names.value_name_of_object(obj))
726 }
727
728 pub fn object_name_key(&self, obj: ObjKey) -> Option<NameKey> {
730 self.names.object_value(obj).copied()
731 }
732
733 pub fn kind_store(&self) -> &KindStore<ObjKey, K> {
735 &self.kinds
736 }
737
738 pub fn kind_field_names(&self) -> impl Iterator<Item = &K> {
740 self.kinds.field_names()
741 }
742
743 pub fn kind_field_keys(&self) -> impl Iterator<Item = KindKey> {
745 self.kinds.field_keys()
746 }
747
748 pub fn kind_field_items(&self) -> impl Iterator<Item = (KindKey, &K)> {
750 self.kinds.field_items()
751 }
752
753 pub fn object_kind<Id: AsRef<Uuid>>(&self, id: Id) -> Option<&K> {
755 self.object_key(id)
756 .and_then(|obj| self.kinds.value_name_of_object(obj))
757 }
758
759 pub fn object_kind_key(&self, obj: ObjKey) -> Option<KindKey> {
761 self.kinds.object_value(obj).copied()
762 }
763
764 pub fn label_store(&self) -> &LabelStore<ObjKey, L> {
766 &self.labels
767 }
768
769 pub fn label_field_names(&self) -> impl Iterator<Item = &L> {
771 self.labels.field_names()
772 }
773
774 pub fn label_field_keys(&self) -> impl Iterator<Item = LabelKey> {
776 self.labels.field_keys()
777 }
778
779 pub fn label_field_items(&self) -> impl Iterator<Item = (LabelKey, &L)> {
781 self.labels.field_items()
782 }
783
784 pub fn object_labels<Id: AsRef<Uuid>>(&self, id: Id) -> impl Iterator<Item = &L> {
786 self.object_key(id)
787 .into_iter()
788 .flat_map(|obj| self.labels.object_field_names(obj))
789 }
790
791 pub fn object_label_keys(&self, obj: ObjKey) -> Option<&BTreeSet<LabelKey>> {
793 self.labels.object_value(obj)
794 }
795
796 pub fn weight_store(&self) -> &WeightStore<ObjKey, W, WV> {
798 &self.weights
799 }
800
801 pub fn weight_field_names(&self) -> impl Iterator<Item = &W> {
803 self.weights.field_names()
804 }
805
806 pub fn weight_field_keys(&self) -> impl Iterator<Item = WeightKey> {
808 self.weights.field_keys()
809 }
810
811 pub fn weight_field_items(&self) -> impl Iterator<Item = (WeightKey, &W)> {
813 self.weights.field_items()
814 }
815
816 pub fn object_weights<Id: AsRef<Uuid>>(&self, id: Id) -> BTreeMap<&W, &WV> {
818 self.object_key(id)
819 .map(|obj| self.weights.named_value_of_object(obj))
820 .unwrap_or_default()
821 }
822
823 pub fn object_weights_keyed(&self, obj: ObjKey) -> Option<&BTreeMap<WeightKey, WV>> {
825 self.weights.object_value(obj)
826 }
827
828 pub fn object_weight<Id: AsRef<Uuid>>(&self, id: Id, weight: &W) -> Option<&WV> {
830 self.object_key(id)
831 .and_then(|obj| self.weights.field_name_value_for_object(obj, weight))
832 }
833
834 pub fn object_weight_by_key(&self, obj: ObjKey, weight: WeightKey) -> Option<&WV> {
836 self.weights.field_value_for_object(obj, weight)
837 }
838
839 pub fn annotation_store(&self) -> &AnnotationStore<ObjKey, A, AV> {
841 &self.annotations
842 }
843
844 pub fn annotation_field_names(&self) -> impl Iterator<Item = &A> {
846 self.annotations.field_names()
847 }
848
849 pub fn annotation_field_keys(&self) -> impl Iterator<Item = AnnotationKey> {
851 self.annotations.field_keys()
852 }
853
854 pub fn annotation_field_items(&self) -> impl Iterator<Item = (AnnotationKey, &A)> {
856 self.annotations.field_items()
857 }
858
859 pub fn object_annotations<Id: AsRef<Uuid>>(&self, id: Id) -> BTreeMap<&A, &AV> {
861 self.object_key(id)
862 .map(|obj| self.annotations.named_value_of_object(obj))
863 .unwrap_or_default()
864 }
865
866 pub fn object_annotations_keyed(&self, obj: ObjKey) -> Option<&BTreeMap<AnnotationKey, AV>> {
868 self.annotations.object_value(obj)
869 }
870
871 pub fn object_annotation<Id: AsRef<Uuid>>(&self, id: Id, annotation: &A) -> Option<&AV> {
873 self.object_key(id).and_then(|obj| {
874 self.annotations
875 .field_name_value_for_object(obj, annotation)
876 })
877 }
878
879 pub fn object_annotation_by_key(&self, obj: ObjKey, annotation: AnnotationKey) -> Option<&AV> {
881 self.annotations.field_value_for_object(obj, annotation)
882 }
883
884 pub fn iter_meta_named<'a>(
886 &'a self,
887 ) -> impl Iterator<Item = MetadataRef<'a, N, K, L, W, WV, A, AV>> {
888 self.object_ids().map(|id| {
889 self.object_meta_named(id).expect(
890 "Object key and UUID pairs should always result in a metadata ref instance.",
891 )
892 })
893 }
894
895 pub fn iter_meta_keyed(
897 &self,
898 ) -> impl Iterator<Item = Metadata<NameKey, KindKey, LabelKey, WeightKey, WV, AnnotationKey, AV>>
899 {
900 self.object_keys().map(|key| {
901 self.object_meta_keyed(key).expect(
902 "Object key and UUID pairs should always result in a metadata ref instance.",
903 )
904 })
905 }
906
907 pub fn aggregate(&self) -> Aggregate<N, K, L, W, WV, A> {
909 self.iter_meta_named()
910 .fold(Aggregate::new(), |mut agg, meta| {
911 agg.add(&meta);
912 agg
913 })
914 }
915}
916
917#[cfg(test)]
918pub mod tests {
919 #[allow(unused_imports)]
920 use pretty_assertions::{assert_eq, assert_ne, assert_str_eq};
921
922 #[allow(unused_imports)]
923 use super::*;
924 pub use crate::metadata::tests::*;
925
926 pub type TestStore = MetadataStore<ObjectKey, N, K, L, W, f64, A, serde_json::Value>;
927
928 impl TestStore {
929 pub fn foobarbazquux() -> Self {
930 let mut store = Self::new();
931
932 TestMeta::foobarbazquux().into_iter().for_each(|meta| {
933 store.insert(meta);
934 });
935
936 store
937 }
938 }
939
940 #[test]
941 fn test_helper_store_empty() {
942 let mut store = TestStore::new();
943
944 assert_eq!(store.len(), 0);
945 assert!(store.is_empty());
946
947 let (key, id) = store.add();
948 assert_eq!(store.object_key(id), Some(key));
949 assert_eq!(store.object_id(key), Some(id));
950 assert_eq!(store.len(), 1);
951 assert!(!store.is_empty());
952 }
953
954 #[test]
955 fn test_helper_store_reads() {
956 let store = TestStore::foobarbazquux();
957
958 assert_eq!(store.len(), 4);
959 assert_eq!(store.object_keys().collect::<BTreeSet<_>>().len(), 4);
960 assert_eq!(store.object_ids().collect::<BTreeSet<_>>().len(), 4);
961 assert_eq!(store.object_key_id_pairs().collect::<Vec<_>>().len(), 4);
962
963 let foo_id = store.objects_with_name(&N::Foo).next().expect("foo id");
964 let foo_key = store.object_key(foo_id).expect("foo key");
965
966 let meta_named = store.object_meta_named(foo_id).expect("metadata ref");
967 assert_eq!(meta_named.name, Some(&N::Foo));
968 assert_eq!(meta_named.kind, Some(&K::A));
969 assert_eq!(meta_named.labels, bon::set![&L::A, &L::B]);
970 assert_eq!(meta_named.weights, bon::map! { &W::A: &1.0 });
971 assert_eq!(
972 meta_named.annotations,
973 bon::map! {&A::A: &serde_json::Value::String("FOO".to_string())}
974 );
975
976 let meta_keyed = store.object_meta_keyed(foo_key).expect("metadata keyed");
977 assert_eq!(
978 meta_keyed.name,
979 store.name_store().field_key_interned(&N::Foo)
980 );
981 }
982
983 #[test]
984 fn test_aggregate() {
985 let store = TestStore::foobarbazquux();
986 let aggregate = store.aggregate();
987
988 assert_eq!(
989 aggregate.names,
990 bon::map! { N::Foo: 1usize, N::Bar: 1usize, N::Baz: 1usize, N::Quux: 1usize },
991 "names"
992 );
993 assert_eq!(
994 aggregate.kinds,
995 bon::map! { K::A: 1usize, K::B: 1usize, K::C: 1usize },
996 "kinds"
997 );
998 assert_eq!(
999 aggregate.labels,
1000 bon::map! { L::A: 1usize, L::B: 2usize, L::C: 2usize },
1001 "labels"
1002 );
1003 assert_eq!(
1004 aggregate.weights,
1005 bon::map! { W::A: 1.0, W::B: 4.0},
1006 "weights"
1007 );
1008 assert_eq!(
1009 aggregate.annotations,
1010 bon::map! { A::A: 4usize, A::B: 2usize },
1011 "annotations"
1012 );
1013 }
1014}