///|
// Map state with last-write-wins semantics.
pub struct MapValue {
value : @types.LoroValue?
lamport : @types.Lamport
peer : @types.PeerID
} derive(Show)
///|
pub fn MapValue::new(
value : @types.LoroValue?,
lamport : @types.Lamport,
peer : @types.PeerID,
) -> MapValue {
MapValue::{ value, lamport, peer }
}
///|
pub struct MapState {
entries : Map[String, MapValue]
child_containers : Map[@types.ContainerID, String]
} derive(Show)
///|
pub fn MapState::new() -> MapState {
MapState::{ entries: Map::new(), child_containers: Map::new() }
}
///|
pub fn MapState::get(self : MapState, key : String) -> @types.LoroValue? {
match self.entries.get(key) {
Some(map_value) => map_value.value
None => None
}
}
///|
pub fn MapState::set(
self : MapState,
key : String,
value : @types.LoroValue,
id : @types.IdFull,
) -> Unit {
let next = MapValue::{
value: Some(value),
lamport: id.lamport,
peer: id.peer,
}
match self.entries.get(key) {
Some(existing) =>
if map_value_newer(next, existing) {
self.update_entry(key, next)
}
None => self.update_entry(key, next)
}
}
///|
pub fn MapState::delete(
self : MapState,
key : String,
id : @types.IdFull,
) -> Unit {
let next = MapValue::{ value: None, lamport: id.lamport, peer: id.peer }
match self.entries.get(key) {
Some(existing) =>
if map_value_newer(next, existing) {
self.update_entry(key, next)
}
None => self.update_entry(key, next)
}
}
///|
pub fn MapState::to_value(self : MapState) -> @types.LoroValue {
let out : Map[String, @types.LoroValue] = {}
for key, map_value in self.entries {
match map_value.value {
Some(v) => out[key] = v
None => ()
}
}
@types.LoroValue::Map(out)
}
///|
pub fn MapState::entry_items(self : MapState) -> Array[(String, MapValue)] {
let out : Array[(String, MapValue)] = []
for key, value in self.entries {
out.push((key, value))
}
out
}
///|
pub fn MapState::insert_entry(
self : MapState,
key : String,
value : MapValue,
) -> Unit {
self.update_entry(key, value)
}
///|
fn map_value_newer(next : MapValue, existing : MapValue) -> Bool {
if next.lamport > existing.lamport {
true
} else if next.lamport < existing.lamport {
false
} else {
next.peer >= existing.peer
}
}
///|
pub fn MapState::update_entry(
self : MapState,
key : String,
next : MapValue,
) -> Unit {
match self.entries.get(key) {
Some(prev) =>
match prev.value {
Some(@types.LoroValue::Container(id)) =>
self.child_containers.remove(id)
_ => ()
}
None => ()
}
match next.value {
Some(@types.LoroValue::Container(id)) => self.child_containers[id] = key
_ => ()
}
self.entries[key] = next
}