tirea_state/
doc_cell.rs

1//! Shared mutable document for write-through-read state access.
2//!
3//! `DocCell` wraps a `Mutex<Value>` so that state writes (via `PatchSink` hooks)
4//! immediately update the document and subsequent reads see the latest values.
5
6use crate::apply::apply_op;
7use crate::{Op, TireaResult};
8use serde_json::Value;
9use std::sync::{Mutex, MutexGuard};
10
11/// Shared mutable document for write-through-read state access.
12///
13/// All state reads lock briefly to fetch the current value, and all writes
14/// apply the operation in-place so the next read sees the update.
15pub struct DocCell(Mutex<Value>);
16
17impl DocCell {
18    /// Create a new `DocCell` with the given initial value.
19    pub fn new(value: Value) -> Self {
20        Self(Mutex::new(value))
21    }
22
23    /// Acquire a read lock on the document.
24    ///
25    /// The returned guard dereferences to `&Value`. Callers should clone
26    /// any needed data before dropping the guard.
27    #[inline]
28    pub fn get(&self) -> MutexGuard<'_, Value> {
29        self.0.lock().unwrap()
30    }
31
32    /// Apply a single operation to the document in-place.
33    ///
34    /// Apply an operation and return any validation/type error.
35    pub fn apply(&self, op: &Op) -> TireaResult<()> {
36        let mut guard = self
37            .0
38            .lock()
39            .map_err(|_| crate::TireaError::invalid_operation("state document mutex poisoned"))?;
40        apply_op(&mut guard, op)
41    }
42
43    /// Consume the `DocCell` and return the inner value.
44    pub fn into_inner(self) -> Value {
45        self.0.into_inner().unwrap()
46    }
47
48    /// Clone the current document value.
49    pub fn snapshot(&self) -> Value {
50        self.get().clone()
51    }
52}
53
54impl Default for DocCell {
55    fn default() -> Self {
56        Self::new(Value::Object(Default::default()))
57    }
58}
59
60impl Clone for DocCell {
61    fn clone(&self) -> Self {
62        Self::new(self.snapshot())
63    }
64}
65
66impl std::fmt::Debug for DocCell {
67    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68        f.debug_tuple("DocCell").field(&"<Value>").finish()
69    }
70}