use std::borrow::Cow;
use std::collections::{BTreeMap, BTreeSet};
use std::fmt::Debug;
use slotmap::{Key, new_key_type};
use snafu::prelude::*;
use uuid::Uuid;
use crate::aggregate::Aggregate;
use crate::category::Category;
use crate::interner::{DenseSlottedBTreeInterner, Interner, ValueAlreadyExistsSnafu};
use crate::metadata::{AnnotationValue, Field, Metadata, MetadataRef, WeightValue};
use crate::transaction::{Transaction, TransactionMode};
#[derive(Clone, Debug, Snafu)]
#[snafu(visibility(pub))]
pub enum Error {
InternerError { source: crate::interner::Error },
}
new_key_type! {
pub struct ObjectKey;
pub struct NameKey;
pub struct KindKey;
pub struct LabelKey;
pub struct WeightKey;
pub struct AnnotationKey;
}
pub type NameStore<ObjKey, Name> = Category<ObjKey, NameKey, NameKey, Name>;
pub type KindStore<ObjKey, Kind> = Category<ObjKey, KindKey, KindKey, Kind>;
pub type LabelStore<ObjKey, Label> = Category<ObjKey, BTreeSet<LabelKey>, LabelKey, Label>;
pub type WeightStore<ObjKey, Weight, Value> =
Category<ObjKey, BTreeMap<WeightKey, Value>, WeightKey, Weight>;
pub type AnnotationStore<ObjKey, Annotation, SubValue> =
Category<ObjKey, BTreeMap<AnnotationKey, SubValue>, AnnotationKey, Annotation>;
pub type SimpleStore =
MetadataStore<ObjectKey, String, String, String, String, f64, String, serde_json::Value>;
#[derive(Clone, Debug, bon::Builder)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(default, rename_all = "camelCase")
)]
#[cfg_attr(feature = "reactive", derive(reactive_stores::Store))]
pub struct MetadataStore<ObjKey, N, K, L, W, WV, A, AV>
where
ObjKey: Key,
N: Field,
K: Field,
L: Field,
W: Field,
WV: WeightValue,
A: Field,
AV: AnnotationValue,
{
ids: DenseSlottedBTreeInterner<ObjKey, Uuid>,
names: NameStore<ObjKey, N>,
kinds: KindStore<ObjKey, K>,
labels: LabelStore<ObjKey, L>,
weights: WeightStore<ObjKey, W, WV>,
annotations: AnnotationStore<ObjKey, A, AV>,
}
impl<ObjKey, N, K, L, W, WV, A, AV> Default for MetadataStore<ObjKey, N, K, L, W, WV, A, AV>
where
ObjKey: Key,
N: Field,
K: Field,
L: Field,
W: Field,
WV: WeightValue,
A: Field,
AV: AnnotationValue,
{
fn default() -> Self {
Self {
ids: Default::default(),
names: Default::default(),
kinds: Default::default(),
labels: Default::default(),
weights: Default::default(),
annotations: Default::default(),
}
}
}
impl<ObjKey, N, K, L, W, WV, A, AV> MetadataStore<ObjKey, N, K, L, W, WV, A, AV>
where
ObjKey: Key,
N: Field,
K: Field,
L: Field,
W: Field,
WV: WeightValue,
A: Field,
AV: AnnotationValue,
{
pub fn new() -> Self {
Self::default()
}
pub fn len(&self) -> usize {
self.ids.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn add(&mut self) -> (ObjKey, Uuid) {
let id = Uuid::new_v4();
(self.intern_id(Cow::Borrowed(&id)), id)
}
pub fn intern_id(&mut self, id: Cow<'_, Uuid>) -> ObjKey {
self.ids.intern(id)
}
pub fn try_insert(&mut self, meta: Metadata<N, K, L, W, WV, A, AV>) -> Result<ObjKey, Error> {
let obj = self
.ids
.try_intern(Cow::Borrowed(&meta.id))
.context(InternerSnafu)?;
self.insert(meta);
Ok(obj)
}
pub fn insert(&mut self, meta: Metadata<N, K, L, W, WV, A, AV>) -> ObjKey {
let id = self.ids.intern_borrowed(&meta.id);
let mut tx = meta.as_replace_transaction();
tx.strip_id();
self.apply(id, tx);
id
}
pub fn try_apply<Id: AsRef<Uuid>>(
&mut self,
id: Id,
tx: Transaction<N, K, L, W, WV, A, AV>,
) -> Result<(), Error> {
let obj = self.ids.try_get_key(id.as_ref()).context(InternerSnafu)?;
if let Some(new_id) = tx.id.as_ref()
&& self.ids.get_key(new_id).as_ref() != Some(&obj)
{
ValueAlreadyExistsSnafu.fail().context(InternerSnafu)?;
}
self.apply(obj, tx);
Ok(())
}
pub fn apply(&mut self, obj: ObjKey, tx: Transaction<N, K, L, W, WV, A, AV>) {
let Transaction {
mode,
id: new_id,
name,
kind,
labels,
weights,
annotations,
} = tx;
if let Some(new_id) = new_id
&& self.ids.get_key(&new_id).as_ref() != Some(&obj)
{
self.ids.remove_key(obj);
self.ids.intern_owned(new_id);
}
if name.is_some() {
self.names
.set_value_name_of_object(obj, name.map(Cow::Owned));
}
if kind.is_some() {
self.kinds
.set_value_name_of_object(obj, kind.map(Cow::Owned));
}
if let Some(labels) = labels {
if matches!(mode, TransactionMode::Replace) {
self.labels.remove_object(obj);
}
labels.into_iter().for_each(|label| {
self.labels
.insert_field_name_for_object(obj, Cow::Owned(label));
});
}
if let Some(weights) = weights {
if matches!(mode, TransactionMode::Replace) {
self.weights.remove_object(obj);
}
weights.into_iter().for_each(|(k, v)| {
self.weights
.insert_field_name_value_for_object(obj, Cow::Owned(k), v);
});
}
if let Some(annotations) = annotations {
if matches!(mode, TransactionMode::Replace) {
self.annotations.remove_object(obj);
}
annotations.into_iter().for_each(|(k, v)| {
self.annotations
.insert_field_name_value_for_object(obj, Cow::Owned(k), v);
});
};
}
pub fn try_apply_keyed(
&mut self,
obj: ObjKey,
tx: Transaction<NameKey, KindKey, LabelKey, WeightKey, WV, AnnotationKey, AV>,
) -> Result<(), Error> {
if let Some(new_id) = tx.id.as_ref()
&& self.ids.get_key(new_id).as_ref() != Some(&obj)
{
ValueAlreadyExistsSnafu.fail().context(InternerSnafu)?;
}
self.apply_keyed(obj, tx);
Ok(())
}
pub fn apply_keyed(
&mut self,
obj: ObjKey,
tx: Transaction<NameKey, KindKey, LabelKey, WeightKey, WV, AnnotationKey, AV>,
) {
let Transaction {
mode,
id: new_id,
name,
kind,
labels,
weights,
annotations,
} = tx;
if let Some(new_id) = new_id
&& self.ids.get_key(&new_id).as_ref() != Some(&obj)
{
self.ids.remove_key(obj);
self.ids.intern_owned(new_id);
}
if let Some(name) = name {
self.set_object_name_key(obj, name);
}
if let Some(kind) = kind {
self.set_object_kind_key(obj, kind);
}
if let Some(labels) = labels {
if matches!(mode, TransactionMode::Replace) {
self.labels.remove_object(obj);
}
labels.into_iter().for_each(|label| {
self.set_object_label_key(obj, label);
});
}
if let Some(weights) = weights {
if matches!(mode, TransactionMode::Replace) {
self.weights.remove_object(obj);
}
weights.into_iter().for_each(|(k, v)| {
self.set_object_weight_key(obj, k, v);
});
}
if let Some(annotations) = annotations {
if matches!(mode, TransactionMode::Replace) {
self.annotations.remove_object(obj);
}
annotations.into_iter().for_each(|(k, v)| {
self.set_object_annotation_key(obj, k, v);
});
};
}
pub fn intern_name(&mut self, name: Cow<'_, N>) -> NameKey {
self.names.field_key(name)
}
pub fn forget_name(&mut self, name: &N) -> Option<NameKey> {
self.names.remove_field_name(name)
}
pub fn forget_name_key(&mut self, name: NameKey) -> Option<N> {
self.names.remove_field(name)
}
pub fn set_object_name<Id: AsRef<Uuid>>(&mut self, id: Id, name: &N) {
let obj = self.intern_id(Cow::Borrowed(id.as_ref()));
let value = self.intern_name(Cow::Borrowed(name));
self.set_object_name_key(obj, value);
}
pub fn set_object_name_key(&mut self, obj: ObjKey, name: NameKey) {
self.names.insert_object_value(obj, name);
}
pub fn remove_object_name<Id: AsRef<Uuid>>(&mut self, id: Id) -> Option<&N> {
let obj = self.ids.get_key(id.as_ref())?;
self.remove_object_name_key(obj)
.and_then(|key| self.names.field_name(key))
}
pub fn remove_object_name_key(&mut self, obj: ObjKey) -> Option<NameKey> {
self.names.remove_object(obj)
}
pub fn intern_kind(&mut self, kind: Cow<K>) -> KindKey {
self.kinds.field_key(kind)
}
pub fn forget_kind(&mut self, kind: &K) -> Option<KindKey> {
self.kinds.remove_field_name(kind)
}
pub fn forget_kind_key(&mut self, kind: KindKey) -> Option<K> {
self.kinds.remove_field(kind)
}
pub fn set_object_kind<Id: AsRef<Uuid>>(&mut self, id: Id, kind: &K) {
let obj = self.intern_id(Cow::Borrowed(id.as_ref()));
let value = self.intern_kind(Cow::Borrowed(kind));
self.set_object_kind_key(obj, value);
}
pub fn set_object_kind_key(&mut self, obj: ObjKey, kind: KindKey) {
self.kinds.insert_object_value(obj, kind);
}
pub fn remove_object_kind<Id: AsRef<Uuid>>(&mut self, id: Id) -> Option<&K> {
let obj = self.ids.get_key(id.as_ref())?;
self.remove_object_kind_key(obj)
.and_then(|key| self.kinds.field_name(key))
}
pub fn remove_object_kind_key(&mut self, obj: ObjKey) -> Option<KindKey> {
self.kinds.remove_object(obj)
}
pub fn intern_label(&mut self, label: Cow<L>) -> LabelKey {
self.labels.field_key(label)
}
pub fn forget_label(&mut self, label: &L) -> Option<LabelKey> {
self.labels.remove_field_name(label)
}
pub fn forget_label_key(&mut self, label: LabelKey) -> Option<L> {
self.labels.remove_field(label)
}
pub fn set_object_label<Id: AsRef<Uuid>>(&mut self, id: Id, label: &L) {
let obj = self.intern_id(Cow::Borrowed(id.as_ref()));
self.labels
.insert_field_name_for_object(obj, Cow::Borrowed(label));
}
pub fn set_object_label_key(&mut self, obj: ObjKey, label: LabelKey) {
self.labels.insert_field_for_object(obj, label);
}
pub fn remove_object_label<Id: AsRef<Uuid>>(&mut self, id: Id, label: &L) {
if let Some(obj) = self.ids.get_key(id.as_ref()) {
self.labels.remove_field_name_from_object(obj, label);
}
}
pub fn strip_label_key(&mut self, obj: ObjKey, label: LabelKey) {
self.labels.remove_field_from_object(obj, label);
}
pub fn intern_weight(&mut self, weight: Cow<W>) -> WeightKey {
self.weights.field_key(weight)
}
pub fn forget_weight(&mut self, weight: &W) -> Option<WeightKey> {
self.weights.remove_field_name_values(weight)
}
pub fn forget_weight_key(&mut self, weight: WeightKey) -> Option<W> {
self.weights.remove_field_values(weight)
}
pub fn set_object_weight<Id: AsRef<Uuid>>(
&mut self,
id: Id,
weight: &W,
value: WV,
) -> Option<WV> {
let obj = self.intern_id(Cow::Borrowed(id.as_ref()));
self.weights
.insert_field_name_value_for_object(obj, Cow::Borrowed(weight), value)
}
pub fn set_object_weight_key(&mut self, obj: ObjKey, key: WeightKey, value: WV) -> Option<WV> {
self.weights.insert_field_value_for_object(obj, key, value)
}
pub fn remove_object_weight<Id: AsRef<Uuid>>(&mut self, id: Id, weight: &W) -> Option<WV> {
self.ids.get_key(id.as_ref()).and_then(|obj| {
self.weights
.remove_field_name_value_from_object(obj, weight)
})
}
pub fn remove_object_weight_key(&mut self, obj: ObjKey, weight: WeightKey) -> Option<WV> {
self.weights.remove_field_value_from_object(obj, weight)
}
pub fn intern_annotation(&mut self, annotation: Cow<A>) -> AnnotationKey {
self.annotations.field_key(annotation)
}
pub fn forget_annotation(&mut self, annotation: &A) -> Option<AnnotationKey> {
self.annotations.remove_field_name_values(annotation)
}
pub fn forget_annotation_key(&mut self, key: AnnotationKey) -> Option<A> {
self.annotations.remove_field_values(key)
}
pub fn set_object_annotation<Id: AsRef<Uuid>>(
&mut self,
id: Id,
annotation: &A,
value: AV,
) -> Option<AV> {
let obj = self.intern_id(Cow::Borrowed(id.as_ref()));
self.annotations
.insert_field_name_value_for_object(obj, Cow::Borrowed(annotation), value)
}
pub fn set_object_annotation_key(
&mut self,
obj: ObjKey,
key: AnnotationKey,
value: AV,
) -> Option<AV> {
self.annotations
.insert_field_value_for_object(obj, key, value)
}
pub fn remove_object_annotation<Id: AsRef<Uuid>>(
&mut self,
id: Id,
annotation: &A,
) -> Option<AV> {
self.ids.get_key(id.as_ref()).and_then(|obj| {
self.annotations
.remove_field_name_value_from_object(obj, annotation)
})
}
pub fn remove_object_annotation_key(
&mut self,
obj: ObjKey,
annotation: AnnotationKey,
) -> Option<AV> {
self.annotations
.remove_field_value_from_object(obj, annotation)
}
pub fn remove_object<Id: AsRef<Uuid>>(&mut self, id: Id) -> bool {
match self.ids.remove_value(id.as_ref()) {
Some(obj) => self.remove_object_key(obj),
None => false,
}
}
pub fn remove_object_key(&mut self, obj: ObjKey) -> bool {
self.ids.remove_key(obj);
let did_name = self.names.remove_object(obj).is_some();
let did_kind = self.kinds.remove_object(obj).is_some();
let did_labels = self.labels.remove_object(obj).is_some();
let did_weights = self.weights.remove_object(obj).is_some();
let did_annotations = self.annotations.remove_object(obj).is_some();
did_name | did_kind | did_labels | did_weights | did_annotations
}
pub fn clean(&mut self) {
self.names.clean();
self.kinds.clean();
self.labels.clean();
self.weights.clean();
self.annotations.clean();
}
pub fn object_meta_named<'a, Id: AsRef<Uuid>>(
&'a self,
id: Id,
) -> Option<MetadataRef<'a, N, K, L, W, WV, A, AV>> {
let id = *id.as_ref();
self.ids.get_key(&id).map(|obj| {
MetadataRef::builder()
.id(id)
.maybe_name(self.names.value_name_of_object(obj))
.maybe_kind(self.kinds.value_name_of_object(obj))
.labels(self.labels.named_value_of_object(obj))
.weights(self.weights.named_value_of_object(obj))
.annotations(self.annotations.named_value_of_object(obj))
.build()
})
}
pub fn object_meta_keyed(
&self,
obj: ObjKey,
) -> Option<Metadata<NameKey, KindKey, LabelKey, WeightKey, WV, AnnotationKey, AV>> {
self.object_id(obj).map(|id| {
Metadata::builder()
.id(id)
.maybe_name(self.object_name_key(obj))
.maybe_kind(self.object_kind_key(obj))
.maybe_labels(self.object_label_keys(obj).cloned())
.maybe_weights(self.object_weights_keyed(obj).cloned())
.maybe_annotations(self.object_annotations_keyed(obj).cloned())
.build()
})
}
pub fn object_keys(&self) -> impl Iterator<Item = ObjKey> {
self.ids.keys()
}
pub fn object_ids(&self) -> impl Iterator<Item = &Uuid> {
self.ids.values()
}
pub fn object_key_id_pairs(&self) -> impl Iterator<Item = (ObjKey, &Uuid)> {
self.ids.items()
}
pub fn object_id(&self, obj: ObjKey) -> Option<Uuid> {
self.ids.get_value(obj).copied()
}
pub fn object_key<Id: AsRef<Uuid>>(&self, id: Id) -> Option<ObjKey> {
self.ids.get_key(id.as_ref())
}
pub fn name_store(&self) -> &NameStore<ObjKey, N> {
&self.names
}
pub fn name_field_names(&self) -> impl Iterator<Item = &N> {
self.names.field_names()
}
pub fn name_field_keys(&self) -> impl Iterator<Item = NameKey> {
self.names.field_keys()
}
pub fn name_field_items(&self) -> impl Iterator<Item = (NameKey, &N)> {
self.names.field_items()
}
pub fn objects_with_name(&self, name: &N) -> impl Iterator<Item = Uuid> {
self.names
.objects_with_field_name(name)
.into_iter()
.flatten()
.flat_map(|&obj| self.object_id(obj))
}
pub fn objects_with_name_key(&self, name: NameKey) -> impl Iterator<Item = ObjKey> {
self.names
.objects_with_field(name)
.into_iter()
.flatten()
.copied()
}
pub fn object_name<Id: AsRef<Uuid>>(&self, id: Id) -> Option<&N> {
self.object_key(id)
.and_then(|obj| self.names.value_name_of_object(obj))
}
pub fn object_name_key(&self, obj: ObjKey) -> Option<NameKey> {
self.names.object_value(obj).copied()
}
pub fn kind_store(&self) -> &KindStore<ObjKey, K> {
&self.kinds
}
pub fn kind_field_names(&self) -> impl Iterator<Item = &K> {
self.kinds.field_names()
}
pub fn kind_field_keys(&self) -> impl Iterator<Item = KindKey> {
self.kinds.field_keys()
}
pub fn kind_field_items(&self) -> impl Iterator<Item = (KindKey, &K)> {
self.kinds.field_items()
}
pub fn object_kind<Id: AsRef<Uuid>>(&self, id: Id) -> Option<&K> {
self.object_key(id)
.and_then(|obj| self.kinds.value_name_of_object(obj))
}
pub fn object_kind_key(&self, obj: ObjKey) -> Option<KindKey> {
self.kinds.object_value(obj).copied()
}
pub fn label_store(&self) -> &LabelStore<ObjKey, L> {
&self.labels
}
pub fn label_field_names(&self) -> impl Iterator<Item = &L> {
self.labels.field_names()
}
pub fn label_field_keys(&self) -> impl Iterator<Item = LabelKey> {
self.labels.field_keys()
}
pub fn label_field_items(&self) -> impl Iterator<Item = (LabelKey, &L)> {
self.labels.field_items()
}
pub fn object_labels<Id: AsRef<Uuid>>(&self, id: Id) -> impl Iterator<Item = &L> {
self.object_key(id)
.into_iter()
.flat_map(|obj| self.labels.object_field_names(obj))
}
pub fn object_label_keys(&self, obj: ObjKey) -> Option<&BTreeSet<LabelKey>> {
self.labels.object_value(obj)
}
pub fn weight_store(&self) -> &WeightStore<ObjKey, W, WV> {
&self.weights
}
pub fn weight_field_names(&self) -> impl Iterator<Item = &W> {
self.weights.field_names()
}
pub fn weight_field_keys(&self) -> impl Iterator<Item = WeightKey> {
self.weights.field_keys()
}
pub fn weight_field_items(&self) -> impl Iterator<Item = (WeightKey, &W)> {
self.weights.field_items()
}
pub fn object_weights<Id: AsRef<Uuid>>(&self, id: Id) -> BTreeMap<&W, &WV> {
self.object_key(id)
.map(|obj| self.weights.named_value_of_object(obj))
.unwrap_or_default()
}
pub fn object_weights_keyed(&self, obj: ObjKey) -> Option<&BTreeMap<WeightKey, WV>> {
self.weights.object_value(obj)
}
pub fn object_weight<Id: AsRef<Uuid>>(&self, id: Id, weight: &W) -> Option<&WV> {
self.object_key(id)
.and_then(|obj| self.weights.field_name_value_for_object(obj, weight))
}
pub fn object_weight_by_key(&self, obj: ObjKey, weight: WeightKey) -> Option<&WV> {
self.weights.field_value_for_object(obj, weight)
}
pub fn annotation_store(&self) -> &AnnotationStore<ObjKey, A, AV> {
&self.annotations
}
pub fn annotation_field_names(&self) -> impl Iterator<Item = &A> {
self.annotations.field_names()
}
pub fn annotation_field_keys(&self) -> impl Iterator<Item = AnnotationKey> {
self.annotations.field_keys()
}
pub fn annotation_field_items(&self) -> impl Iterator<Item = (AnnotationKey, &A)> {
self.annotations.field_items()
}
pub fn object_annotations<Id: AsRef<Uuid>>(&self, id: Id) -> BTreeMap<&A, &AV> {
self.object_key(id)
.map(|obj| self.annotations.named_value_of_object(obj))
.unwrap_or_default()
}
pub fn object_annotations_keyed(&self, obj: ObjKey) -> Option<&BTreeMap<AnnotationKey, AV>> {
self.annotations.object_value(obj)
}
pub fn object_annotation<Id: AsRef<Uuid>>(&self, id: Id, annotation: &A) -> Option<&AV> {
self.object_key(id).and_then(|obj| {
self.annotations
.field_name_value_for_object(obj, annotation)
})
}
pub fn object_annotation_by_key(&self, obj: ObjKey, annotation: AnnotationKey) -> Option<&AV> {
self.annotations.field_value_for_object(obj, annotation)
}
pub fn iter_meta_named<'a>(
&'a self,
) -> impl Iterator<Item = MetadataRef<'a, N, K, L, W, WV, A, AV>> {
self.object_ids().map(|id| {
self.object_meta_named(id).expect(
"Object key and UUID pairs should always result in a metadata ref instance.",
)
})
}
pub fn iter_meta_keyed(
&self,
) -> impl Iterator<Item = Metadata<NameKey, KindKey, LabelKey, WeightKey, WV, AnnotationKey, AV>>
{
self.object_keys().map(|key| {
self.object_meta_keyed(key).expect(
"Object key and UUID pairs should always result in a metadata ref instance.",
)
})
}
pub fn aggregate(&self) -> Aggregate<N, K, L, W, WV, A> {
self.iter_meta_named()
.fold(Aggregate::new(), |mut agg, meta| {
agg.add(&meta);
agg
})
}
}
#[cfg(test)]
pub mod tests {
#[allow(unused_imports)]
use pretty_assertions::{assert_eq, assert_ne, assert_str_eq};
#[allow(unused_imports)]
use super::*;
pub use crate::metadata::tests::*;
pub type TestStore = MetadataStore<ObjectKey, N, K, L, W, f64, A, serde_json::Value>;
impl TestStore {
pub fn foobarbazquux() -> Self {
let mut store = Self::new();
TestMeta::foobarbazquux().into_iter().for_each(|meta| {
store.insert(meta);
});
store
}
}
#[test]
fn test_helper_store_empty() {
let mut store = TestStore::new();
assert_eq!(store.len(), 0);
assert!(store.is_empty());
let (key, id) = store.add();
assert_eq!(store.object_key(id), Some(key));
assert_eq!(store.object_id(key), Some(id));
assert_eq!(store.len(), 1);
assert!(!store.is_empty());
}
#[test]
fn test_helper_store_reads() {
let store = TestStore::foobarbazquux();
assert_eq!(store.len(), 4);
assert_eq!(store.object_keys().collect::<BTreeSet<_>>().len(), 4);
assert_eq!(store.object_ids().collect::<BTreeSet<_>>().len(), 4);
assert_eq!(store.object_key_id_pairs().collect::<Vec<_>>().len(), 4);
let foo_id = store.objects_with_name(&N::Foo).next().expect("foo id");
let foo_key = store.object_key(foo_id).expect("foo key");
let meta_named = store.object_meta_named(foo_id).expect("metadata ref");
assert_eq!(meta_named.name, Some(&N::Foo));
assert_eq!(meta_named.kind, Some(&K::A));
assert_eq!(meta_named.labels, bon::set![&L::A, &L::B]);
assert_eq!(meta_named.weights, bon::map! { &W::A: &1.0 });
assert_eq!(
meta_named.annotations,
bon::map! {&A::A: &serde_json::Value::String("FOO".to_string())}
);
let meta_keyed = store.object_meta_keyed(foo_key).expect("metadata keyed");
assert_eq!(
meta_keyed.name,
store.name_store().field_key_interned(&N::Foo)
);
}
#[test]
fn test_aggregate() {
let store = TestStore::foobarbazquux();
let aggregate = store.aggregate();
assert_eq!(
aggregate.names,
bon::map! { N::Foo: 1usize, N::Bar: 1usize, N::Baz: 1usize, N::Quux: 1usize },
"names"
);
assert_eq!(
aggregate.kinds,
bon::map! { K::A: 1usize, K::B: 1usize, K::C: 1usize },
"kinds"
);
assert_eq!(
aggregate.labels,
bon::map! { L::A: 1usize, L::B: 2usize, L::C: 2usize },
"labels"
);
assert_eq!(
aggregate.weights,
bon::map! { W::A: 1.0, W::B: 4.0},
"weights"
);
assert_eq!(
aggregate.annotations,
bon::map! { A::A: 4usize, A::B: 2usize },
"annotations"
);
}
}