use super::ConflictedValue;
use crate::crdts::TypeVariantValue;
use crate::{ExtensionType, MvReg, OrArray, OrMap};
use std::{fmt, hash::Hash};
#[derive(Debug)]
pub enum CrdtValue<'tx, K, C = crate::crdts::NoExtensionTypes>
where
K: Hash + Eq + fmt::Debug + Clone,
C: ExtensionType,
{
Map(&'tx OrMap<String, C>),
Array(&'tx OrArray<C>),
Register(&'tx MvReg),
Conflicted(ConflictedValue<'tx, K, C>),
Empty,
}
impl<'tx, K, C> CrdtValue<'tx, K, C>
where
K: Hash + Eq + fmt::Debug + Clone,
C: ExtensionType,
{
pub fn from_type_variant(value: &'tx TypeVariantValue<C>) -> Self {
use crate::dotstores::DotStore;
let has_multiple_types = {
let mut count = 0;
if !value.map.is_bottom() {
count += 1;
}
if !value.array.is_bottom() {
count += 1;
}
if !value.reg.is_bottom() {
count += 1;
}
count > 1
};
if has_multiple_types {
CrdtValue::Conflicted(ConflictedValue::new(value))
} else if !value.reg.is_bottom() {
CrdtValue::Register(&value.reg)
} else if !value.map.is_bottom() {
CrdtValue::Map(&value.map)
} else if !value.array.is_bottom() {
CrdtValue::Array(&value.array)
} else {
CrdtValue::Empty
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::crdts::mvreg::MvRegValue;
use crate::crdts::{NoExtensionTypes, TypeVariantValue};
use crate::dotstores::DotStore;
use crate::sentinel::DummySentinel;
use crate::{CausalDotStore, Identifier, OrMap};
#[test]
fn from_type_variant_register_only() {
let store = CausalDotStore::<OrMap<String>>::default();
let id = Identifier::new(0, 0);
let delta = store.store.apply_to_register(
|reg, ctx, id| reg.write(MvRegValue::U64(42), ctx, id),
"key".to_string(),
&store.context,
id,
);
let type_variant = delta.store.get(&"key".to_string()).unwrap();
let value: CrdtValue<'_, String> = CrdtValue::from_type_variant(type_variant);
match value {
CrdtValue::Register(reg) => {
use crate::crdts::snapshot::ToValue;
assert_eq!(reg.value().unwrap(), &MvRegValue::U64(42));
}
_ => panic!("Expected Register variant"),
}
}
#[test]
fn from_type_variant_empty() {
let type_variant = TypeVariantValue::<NoExtensionTypes>::default();
let value: CrdtValue<'_, String> = CrdtValue::from_type_variant(&type_variant);
match value {
CrdtValue::Empty => { }
_ => panic!("Expected Empty variant"),
}
}
#[test]
fn from_type_variant_map_only() {
let store = CausalDotStore::<OrMap<String>>::default();
let id = Identifier::new(0, 0);
let delta = store.store.apply_to_map(
|map, ctx, id| {
map.apply_to_register(
|reg, ctx, id| reg.write(MvRegValue::String("test".to_string()), ctx, id),
"field".to_string(),
ctx,
id,
)
},
"key".to_string(),
&store.context,
id,
);
let type_variant = delta.store.get(&"key".to_string()).unwrap();
let value: CrdtValue<'_, String> = CrdtValue::from_type_variant(type_variant);
match value {
CrdtValue::Map(map) => {
assert!(!map.is_bottom());
}
_ => panic!("Expected Map variant"),
}
}
#[test]
fn from_type_variant_array_only() {
let store = CausalDotStore::<OrMap<String>>::default();
let id = Identifier::new(0, 0);
let delta = store.store.apply_to_array(
|array, ctx, id| array.insert_idx_register(0, MvRegValue::U64(1), ctx, id),
"key".to_string(),
&store.context,
id,
);
let type_variant = delta.store.get(&"key".to_string()).unwrap();
let value: CrdtValue<'_, String> = CrdtValue::from_type_variant(type_variant);
match value {
CrdtValue::Array(array) => {
assert_eq!(array.len(), 1);
}
_ => panic!("Expected Array variant"),
}
}
#[test]
fn from_type_variant_conflicted() {
let store = CausalDotStore::<OrMap<String>>::default();
let id1 = Identifier::new(0, 0);
let id2 = Identifier::new(1, 0);
let delta1 = store.store.apply_to_register(
|reg, ctx, id| reg.write(MvRegValue::U64(42), ctx, id),
"key".to_string(),
&store.context,
id1,
);
let delta2 = store.store.apply_to_array(
|array, ctx, id| {
array.insert_idx_register(0, MvRegValue::String("conflict".to_string()), ctx, id)
},
"key".to_string(),
&store.context,
id2,
);
let combined = delta1.join(delta2, &mut DummySentinel).unwrap();
let type_variant = combined.store.get(&"key".to_string()).unwrap();
let value: CrdtValue<'_, String> = CrdtValue::from_type_variant(type_variant);
match value {
CrdtValue::Conflicted(conflicts) => {
assert!(conflicts.has_register());
assert!(conflicts.has_array());
assert_eq!(conflicts.conflict_count(), 2);
}
_ => panic!("Expected Conflicted variant, got {value:?}"),
}
}
}