Development Documentation (main branch) - For stable release docs, see docs.rs/eidetica

YDoc

Struct YDoc 

Source
pub struct YDoc { /* private fields */ }
Expand description

A Y-CRDT based Store implementation with efficient differential saving.

YDoc provides a CRDT-based storage abstraction using the yrs library, which is a Rust port of Yjs. This allows for real-time collaborative editing and automatic conflict resolution through the Y-CRDT algorithms.

§Architecture

The YDoc integrates with Eidetica’s transaction system to provide:

  • Differential Updates: Only saves incremental changes, not full document state
  • Efficient Caching: Caches expensive backend data retrieval operations
  • Transaction/Viewer Model: Compatible with Eidetica’s transaction patterns
  • Full Y-CRDT API: Direct access to the complete yrs library functionality

§Caching Strategy

To optimize performance, YDoc caches the expensive get_full_state() operation from the backend and constructs documents and state vectors on-demand from this cached data. This approach minimizes I/O operations while keeping memory usage low.

§Differential Saving

When saving documents, YDoc calculates diffs relative to the current backend state rather than saving full document snapshots. This significantly reduces storage overhead for large documents with incremental changes.

§Usage

The YDoc exposes the underlying Y-CRDT document directly, allowing users to work with the full yrs API. Changes are automatically captured and stored when the transaction is committed.

use eidetica::store::YDoc;
use yrs::{Map, Text, Transact};
// Work directly with the yrs document
store.with_doc_mut(|doc| {
    let map = doc.get_or_insert_map("root");
    let text = doc.get_or_insert_text("document");

    let mut txn = doc.transact_mut();
    map.insert(&mut txn, "key", "value");
    text.insert(&mut txn, 0, "Hello, World!");

    Ok(())
}).await?;

Implementations§

Source§

impl YDoc

Source

pub async fn doc(&self) -> Result<Doc>

Gets the current Y-CRDT document, merging all historical state.

This method reconstructs the current state of the Y-CRDT document by:

  1. Loading the full historical state from the backend (cached)
  2. Applying any local changes from the current transaction
  3. Returning a Y-Doc that can be used for reading and further modifications
§Performance

The expensive backend data retrieval is cached, so subsequent calls are fast. Documents are constructed fresh each time to ensure isolation between operations.

§Returns

A Result containing the merged Doc (Y-CRDT document).

§Errors

Returns an error if there are issues deserializing the Y-CRDT updates.

Source

pub async fn with_doc<F, R>(&self, f: F) -> Result<R>
where F: FnOnce(&Doc) -> Result<R>,

Executes a function with read-only access to the Y-Doc.

This method provides access to the current state of the document for read-only operations. No changes are persisted.

§Arguments
  • f - A function that receives the Y-Doc for reading
§Returns

A Result containing the return value of the function.

§Example
let content = store.with_doc(|doc| {
    let text = doc.get_or_insert_text("document");
    let txn = doc.transact();
    Ok(text.get_string(&txn))
}).await?;
Source

pub async fn with_doc_mut<F, R>(&self, f: F) -> Result<R>
where F: FnOnce(&Doc) -> Result<R>,

Executes a function with access to the Y-Doc and automatically saves changes.

This is the preferred way to make changes to the document as it ensures all changes are captured using differential saving and staged in the transaction for later commit.

§Differential Saving

Changes are saved as diffs relative to the current backend state, which significantly reduces storage overhead compared to saving full document snapshots.

§Arguments
  • f - A function that receives the Y-Doc and can make modifications
§Returns

A Result containing the return value of the function.

§Example
store.with_doc_mut(|doc| {
    let text = doc.get_or_insert_text("document");
    let mut txn = doc.transact_mut();
    text.insert(&mut txn, 0, "Hello, World!");
    Ok(())
}).await?;
Source

pub async fn apply_update(&self, update_data: &[u8]) -> Result<()>

Applies a Y-CRDT update to the document.

This method is useful for receiving updates from other collaborators or applying updates received through a network provider. The update is applied to the current document state and saved using differential saving.

§Use Cases
  • Applying updates from remote collaborators
  • Synchronizing with external Y-CRDT instances
  • Replaying historical updates
§Arguments
  • update_data - The binary Y-CRDT update data
§Returns

A Result<()> indicating success or failure.

§Errors

Returns an error if the update data is malformed or cannot be applied.

Source

pub async fn get_update(&self) -> Result<Vec<u8>>

Gets the current state of the document as a binary update.

This method encodes the complete current document state as a Y-CRDT binary update that can be used to synchronize with other instances or persist the current state.

§Use Cases
  • Synchronizing document state with other instances
  • Creating snapshots of the current state
  • Sharing the complete document with new collaborators
§Returns

A Result containing the binary update data representing the full document state.

§Performance

This method constructs the full document state, so it may be expensive for large documents. For incremental synchronization, consider using the differential updates automatically saved by with_doc_mut().

Source

pub async fn save_doc_full(&self, doc: &Doc) -> Result<()>

Saves the complete document state to the transaction.

This method captures the entire current state of the document and stages it in the transaction. Unlike save_doc(), this saves the full document state rather than just the incremental changes.

§When to Use
  • When you need to ensure the complete state is captured
  • For creating clean snapshots without incremental history
  • When differential saving is not suitable for your use case
§Performance Impact

This method is less storage-efficient than save_doc() as it saves the complete document state regardless of what changes were made.

§Arguments
  • doc - The Y-CRDT document to save
§Returns

A Result<()> indicating success or failure.

Source

pub async fn save_doc(&self, doc: &Doc) -> Result<()>

Saves the document state using efficient differential encoding.

This method captures only the changes since the current backend state and stages them in the transaction. This is the preferred saving method as it significantly reduces storage overhead for incremental changes.

§Differential Encoding

The method works by:

  1. Getting the current backend state vector (cached for efficiency)
  2. Encoding only the changes since that state
  3. Saving only the incremental diff, not the full document
§Storage Efficiency

For a document with small incremental changes, this can reduce storage requirements by orders of magnitude compared to saving full snapshots.

§Arguments
  • doc - The Y-CRDT document to save differentially
§Returns

A Result<()> indicating success or failure.

§Performance

This method is optimized for performance - the expensive backend state retrieval is cached, and only minimal diff calculation is performed.

Trait Implementations§

Source§

impl Registered for YDoc

Source§

fn type_id() -> &'static str

Returns a unique identifier for this type. Read more
Source§

fn supports_type_id(type_id: &str) -> bool

Check if this type supports loading from a stored type_id. Read more
Source§

impl Store for YDoc

Source§

type Data = YrsBinary

The CRDT data type used for local (staged) data in this store. Read more
Source§

fn new<'life0, 'async_trait>( txn: &'life0 Transaction, subtree_name: String, ) -> Pin<Box<dyn Future<Output = Result<Self>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Creates a new Store handle associated with a specific transaction. Read more
Source§

fn name(&self) -> &str

Returns the name of this subtree.
Source§

fn transaction(&self) -> &Transaction

Returns a reference to the transaction this Store is associated with. Read more
Source§

fn default_config() -> Doc

Returns the default configuration for this Store type as a Doc. Read more
Source§

fn init<'life0, 'async_trait>( txn: &'life0 Transaction, subtree_name: String, ) -> Pin<Box<dyn Future<Output = Result<Self>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Initializes a new subtree and registers it in the _index. Read more
Source§

fn get_config<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = Result<Doc>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Gets the current configuration for this Store from the _index subtree. Read more
Source§

fn set_config<'life0, 'async_trait>( &'life0 self, config: Doc, ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Sets the configuration for this Store in the _index subtree. Read more
Source§

fn get_height_strategy<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = Result<Option<HeightStrategy>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Gets the height strategy for this Store from the _index subtree. Read more
Source§

fn set_height_strategy<'life0, 'async_trait>( &'life0 self, strategy: Option<HeightStrategy>, ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Sets the height strategy for this Store in the _index subtree. Read more
Source§

fn local_data(&self) -> Result<Option<Self::Data>>

Returns the local (staged) data for this store from the current transaction. Read more

Auto Trait Implementations§

§

impl !Freeze for YDoc

§

impl !RefUnwindSafe for YDoc

§

impl Send for YDoc

§

impl Sync for YDoc

§

impl Unpin for YDoc

§

impl !UnwindSafe for YDoc

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<T> CompatExt for T

§

fn compat(self) -> Compat<T>

Applies the [Compat] adapter by value. Read more
§

fn compat_ref(&self) -> Compat<&T>

Applies the [Compat] adapter by shared reference. Read more
§

fn compat_mut(&mut self) -> Compat<&mut T>

Applies the [Compat] adapter by mutable reference. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
§

impl<T> PolicyExt for T
where T: ?Sized,

§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] only if self and other return Action::Follow. Read more
§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more