///|
pub(all) enum EntryChange {
Inserted(Out)
Updated(Out, Out)
Removed(Out)
}
///|
pub fn EntryChange::to_json(self : EntryChange) -> Json {
match self {
Inserted(value) => { "action": "add", "new_value": value.to_json() }
Updated(old, value) =>
{
"action": "update",
"old_value": old.to_json(),
"new_value": value.to_json(),
}
Removed(value) => { "action": "delete", "old_value": value.to_json() }
}
}
///|
pub struct MapEvent {
priv route : ReadOnlyArray[PathSegment]
priv map : MapRef
priv changes : ReadOnlyArray[(String, EntryChange)]
}
///|
pub fn MapEvent::target(self : MapEvent) -> MapRef {
self.map
}
///|
pub fn MapEvent::keys(self : MapEvent) -> Map[String, EntryChange] {
Map::from_array(self.changes.iter().to_array())
}
///|
pub fn MapEvent::to_json(self : MapEvent) -> Json {
let result : Map[String, Json] = Map([])
for (key, change) in self.changes {
result[key] = change.to_json()
}
Json(result)
}
///|
pub fn MapRef::observe(
self : MapRef,
key : String,
callback : (MapEvent, Transaction) -> Unit,
) -> Unit {
let observers = match self.doc.map_observers.get(self.name) {
Some(observers) => observers
None => {
let observers : Observer[String, (MapEvent, Transaction) -> Unit] = Observer()
self.doc.map_observers[self.name] = observers
observers
}
}
observers.subscribe(key, callback)
}
///|
pub fn MapRef::unobserve(self : MapRef, key : String) -> Bool {
guard self.doc.map_observers.get(self.name) is Some(observers) else {
return false
}
let found = observers.unsubscribe(key)
if observers.is_empty() {
self.doc.map_observers.remove(self.name)
}
found
}
///|
fn Doc::touch_map(self : Doc, parent : Parent, key : String) -> Unit {
guard self.active_transaction is Some(transaction) else { return }
let changes = match transaction.map_changes.get(parent) {
Some(changes) => changes
None => {
let changes : Map[String, (Id?, Out?)] = Map([])
transaction.map_changes[parent] = changes
changes
}
}
if !changes.contains(key) {
let map : MapRef = { doc: self, name: parent, }
let old = map.get(key)
let id = if old is Some(_) {
self.branch(parent).values.get(key)
} else {
None
}
changes[key] = (id, old)
}
}
///|
fn Transaction::map_events(self : Transaction) -> Array[MapEvent] {
let events = []
for parent, changed in self.map_changes {
if parent is Nested(id) {
if self.document.find(id) is Some(owner) && owner.deleted {
continue
}
if self.added.any(fn(range) {
range.0 == id.client && range.1 <= id.clock && id.clock < range.2
}) {
continue
}
}
let map : MapRef = { doc: self.document, name: parent, }
let changes : Array[(String, EntryChange)] = []
for key, previous in changed {
let current = map.get(key)
let current_id = if current is Some(_) {
self.document.branch(parent).values.get(key)
} else {
None
}
if current_id == previous.0 {
continue
}
match (previous.1, current) {
(None, Some(value)) => changes.push((key, Inserted(value)))
(Some(old), Some(value)) => changes.push((key, Updated(old, value)))
(Some(old), None) => changes.push((key, Removed(old)))
(None, None) => ()
}
}
changes.sort_by(fn(a, b) { a.0.compare(b.0) })
events.push({
route: [],
map,
changes: ReadOnlyArray::from_array(changes[:]),
})
}
events
}