//! Write path: create, sign, apply, persist, and broadcast a CRDT op. //! //! [`apply_and_persist`] is the single entry point for all CRDT mutations. //! It invokes the caller's op-factory closure, signs the resulting op, applies //! it to the live document, sends it to the persistence channel, and broadcasts //! it to sync peers via [`super::SYNC_TX`]. use bft_json_crdt::json_crdt::JsonValue; use bft_json_crdt::op::Op; use super::super::types::CrdtEvent; use super::{CrdtState, statics}; /// Create a CRDT op via `op_fn`, sign it, apply it, and send it to the /// persistence channel. The closure receives `&mut CrdtState` so it can /// mutably access the CRDT document, while `sign` only needs `&keypair`. pub(in crate::crdt_state) fn apply_and_persist(state: &mut CrdtState, op_fn: F) where F: FnOnce(&mut CrdtState) -> Op, { let raw_op = op_fn(state); let signed = raw_op.sign(&state.keypair); state.crdt.apply(signed.clone()); if state.persist_tx.send(signed.clone()).is_err() { let op_type = if signed.inner.is_deleted { "Delete" } else { "Insert" }; let seq = signed.inner.seq; crate::slog_warn!( "[crdt_persist] persist channel send failed: op_type={op_type} seq={seq}" ); } // Track in ALL_OPS + VECTOR_CLOCK, then broadcast to sync peers. if let Ok(json) = serde_json::to_string(&signed) { statics::track_op(&signed, json); } if let Some(tx) = statics::SYNC_TX.get() { let _ = tx.send(signed); } } /// Broadcast a CRDT event to all subscribers. pub(in crate::crdt_state) fn emit_event(event: CrdtEvent) { if let Some(tx) = statics::CRDT_EVENT_TX.get() { let _ = tx.send(event); } }