pub struct RustSyncManager<FS: AsyncFileSystem> { /* private fields */ }Expand description
Unified sync manager for workspace and body synchronization.
This struct replaces all TypeScript sync bridges with a single unified Rust implementation. It handles:
- Workspace metadata sync via Y-sync protocol
- Per-file body sync via Y-sync protocol
- Sync completion tracking
- Echo detection to avoid processing our own updates
- File locking to prevent concurrent modifications
Implementations§
Source§impl<FS: AsyncFileSystem> RustSyncManager<FS>
impl<FS: AsyncFileSystem> RustSyncManager<FS>
Sourcepub fn new(
workspace_crdt: Arc<WorkspaceCrdt>,
body_manager: Arc<BodyDocManager>,
sync_handler: Arc<SyncHandler<FS>>,
) -> Self
pub fn new( workspace_crdt: Arc<WorkspaceCrdt>, body_manager: Arc<BodyDocManager>, sync_handler: Arc<SyncHandler<FS>>, ) -> Self
Create a new sync manager.
Sourcepub fn set_event_callback(
&self,
callback: Arc<dyn Fn(&FileSystemEvent) + Send + Sync>,
)
pub fn set_event_callback( &self, callback: Arc<dyn Fn(&FileSystemEvent) + Send + Sync>, )
Set the event callback for emitting filesystem events.
This callback is used to emit SendSyncMessage events to TypeScript, which then sends the bytes over WebSocket to the sync server.
Important: This also sets up the body sync observer callback on the body_manager, so that local body changes automatically emit sync messages via the Yrs observer pattern.
Sourcepub fn clear_event_callback(&self)
pub fn clear_event_callback(&self)
Clear the event callback.
Call this when stopping sync to prevent sending to a disconnected channel.
Sourcepub fn emit_workspace_update(&self) -> Result<()>
pub fn emit_workspace_update(&self) -> Result<()>
Create and emit a workspace sync message.
Call this after updating the workspace CRDT to send the changes to the sync server via TypeScript WebSocket.
Sourcepub fn emit_body_update(&self, doc_name: &str, content: &str) -> Result<()>
pub fn emit_body_update(&self, doc_name: &str, content: &str) -> Result<()>
Create and emit a body sync message.
Call this after updating a body CRDT to send the changes to the sync server via TypeScript WebSocket.
IMPORTANT: This assumes the body CRDT has already been updated via set_body(). It only encodes the current state - it does NOT call set_body() again.
The doc_name is the canonical file path (e.g., “workspace/notes.md”).
The content is used only for echo detection tracking.
This method uses delta encoding: it tracks the last-sent state vector and only sends changes since then, not the full document state.
Sourcepub async fn handle_workspace_message(
&self,
message: &[u8],
write_to_disk: bool,
) -> Result<SyncMessageResult>
pub async fn handle_workspace_message( &self, message: &[u8], write_to_disk: bool, ) -> Result<SyncMessageResult>
Handle an incoming WebSocket message for workspace sync.
Returns a SyncMessageResult containing:
- Optional response bytes to send back
- List of changed file paths
- Whether sync is now complete
If write_to_disk is true, changed files will be written to disk
via the SyncHandler.
Sourcepub fn create_workspace_sync_step1(&self) -> Vec<u8> ⓘ
pub fn create_workspace_sync_step1(&self) -> Vec<u8> ⓘ
Create a SyncStep1 message for workspace sync.
Sourcepub fn create_workspace_update(
&self,
since_state_vector: Option<&[u8]>,
) -> Result<Vec<u8>>
pub fn create_workspace_update( &self, since_state_vector: Option<&[u8]>, ) -> Result<Vec<u8>>
Create an update message for local workspace changes.
If since_state_vector is provided, returns only updates since that state.
Otherwise returns the full state.
Sourcepub fn is_workspace_synced(&self) -> bool
pub fn is_workspace_synced(&self) -> bool
Check if workspace sync is complete.
Sourcepub fn init_body_sync(&self, doc_name: &str)
pub fn init_body_sync(&self, doc_name: &str)
Initialize body sync for a document.
Ensures the body document exists and is ready for sync.
Sourcepub fn close_body_sync(&self, doc_name: &str)
pub fn close_body_sync(&self, doc_name: &str)
Close body sync for a document.
Sourcepub fn unload_body_doc(&self, doc_name: &str)
pub fn unload_body_doc(&self, doc_name: &str)
Unload a body doc from memory to free RAM.
Saves the doc to storage first, then evicts it from the in-memory cache.
Clears tracking state (last_known_content, last_sent_body_sv, last_synced_body_svs)
but does NOT clear body_synced — the doc was synced, just evicted.
The doc will be reloaded from storage on next access (get_or_create).
Sourcepub async fn handle_body_message(
&self,
doc_name: &str,
message: &[u8],
write_to_disk: bool,
) -> Result<BodySyncResult>
pub async fn handle_body_message( &self, doc_name: &str, message: &[u8], write_to_disk: bool, ) -> Result<BodySyncResult>
Handle an incoming WebSocket message for body sync.
Returns a BodySyncResult containing:
- Optional response bytes to send back
- New content if it changed
- Whether this is an echo of our own update
Sourcepub fn create_body_sync_step1(&self, doc_name: &str) -> Vec<u8> ⓘ
pub fn create_body_sync_step1(&self, doc_name: &str) -> Vec<u8> ⓘ
Create a SyncStep1 message for body sync.
Sourcepub async fn ensure_body_content_loaded(&self, doc_name: &str) -> Result<bool>
pub async fn ensure_body_content_loaded(&self, doc_name: &str) -> Result<bool>
Ensure body content is populated from disk before sync.
This method reads the file content from disk and sets it into the body CRDT.
It should be called before create_body_sync_step1() to ensure the body
CRDT has content to sync (rather than sending an empty state vector).
Returns true if content was loaded, false if the body doc already had content or the file doesn’t exist.
Sourcepub fn create_body_update(
&self,
doc_name: &str,
content: &str,
) -> Result<Vec<u8>>
pub fn create_body_update( &self, doc_name: &str, content: &str, ) -> Result<Vec<u8>>
Create an update message for local body changes.
Sourcepub fn is_body_synced(&self, doc_name: &str) -> bool
pub fn is_body_synced(&self, doc_name: &str) -> bool
Check if body sync is complete for a document.
Sourcepub fn clear_body_synced(&self, doc_name: &str)
pub fn clear_body_synced(&self, doc_name: &str)
Clear the body_synced flag for a document so it can be re-subscribed.
Sourcepub fn is_body_loaded(&self, doc_name: &str) -> bool
pub fn is_body_loaded(&self, doc_name: &str) -> bool
Check if a body document is currently loaded in memory.
Sourcepub async fn file_exists_for_sync(&self, canonical_path: &str) -> bool
pub async fn file_exists_for_sync(&self, canonical_path: &str) -> bool
Check whether a canonical path currently exists in the backing filesystem.
Sourcepub fn workspace_state_changed(&self) -> bool
pub fn workspace_state_changed(&self) -> bool
Check if workspace state has changed since last successful sync.
Returns true if:
- This is the first sync (no cached state)
- The local state vector differs from the cached state after last SyncStep2
Used to skip sending SyncStep1 on reconnect when state hasn’t changed.
Sourcepub fn body_state_changed(&self, doc_name: &str) -> bool
pub fn body_state_changed(&self, doc_name: &str) -> bool
Check if body doc state has changed since last successful sync.
Returns true if:
- This is the first sync for this doc (no cached state)
- The doc is not loaded yet
- The local state vector differs from the cached state after last SyncStep2
Used to skip sending SyncStep1 on reconnect when state hasn’t changed.
Sourcepub fn is_echo(&self, path: &str, content: &str) -> bool
pub fn is_echo(&self, path: &str, content: &str) -> bool
Check if content change is an echo of our own edit.
Sourcepub fn track_content(&self, path: &str, content: &str)
pub fn track_content(&self, path: &str, content: &str)
Track content for echo detection.
Sourcepub fn clear_tracked_content(&self, path: &str)
pub fn clear_tracked_content(&self, path: &str)
Clear tracked content (e.g., when closing a file).
Sourcepub fn is_metadata_echo(&self, path: &str, metadata: &FileMetadata) -> bool
pub fn is_metadata_echo(&self, path: &str, metadata: &FileMetadata) -> bool
Check if metadata change is an echo of our own edit (ignoring modified_at).
Sourcepub fn track_metadata(&self, path: &str, metadata: &FileMetadata)
pub fn track_metadata(&self, path: &str, metadata: &FileMetadata)
Track metadata for echo detection.
Sourcepub fn clear_tracked_metadata(&self, path: &str)
pub fn clear_tracked_metadata(&self, path: &str)
Clear tracked metadata (e.g., when closing a file).
Sourcepub fn get_all_file_paths(&self) -> Vec<String>
pub fn get_all_file_paths(&self) -> Vec<String>
Get all active file paths in the workspace CRDT.
Used by SyncClient to initiate body sync for all files after the body connection is established.
Sourcepub fn mark_sync_complete(&self)
pub fn mark_sync_complete(&self)
Mark initial sync as complete.
Sourcepub fn is_sync_complete(&self) -> bool
pub fn is_sync_complete(&self) -> bool
Check if initial sync is complete.
Sourcepub fn get_active_syncs(&self) -> Vec<String>
pub fn get_active_syncs(&self) -> Vec<String>
Get list of body docs that have completed initial sync.
Sourcepub fn set_focused_files(&self, files: &[String])
pub fn set_focused_files(&self, files: &[String])
Set the files this client is focused on.
This is used to track focus for reconnection - when the client reconnects, it should re-focus on these files.
Sourcepub fn get_focused_files(&self) -> Vec<String>
pub fn get_focused_files(&self) -> Vec<String>
Get the files this client is focused on.
Returns the list of files to re-focus on after reconnection.
Sourcepub fn add_focused_files(&self, files: &[String])
pub fn add_focused_files(&self, files: &[String])
Add files to the focus list.
Sourcepub fn remove_focused_files(&self, files: &[String])
pub fn remove_focused_files(&self, files: &[String])
Remove files from the focus list.
Sourcepub fn is_file_focused(&self, file: &str) -> bool
pub fn is_file_focused(&self, file: &str) -> bool
Check if a file is in the focus list.
Sourcepub fn get_storage_path(&self, canonical_path: &str) -> PathBuf
pub fn get_storage_path(&self, canonical_path: &str) -> PathBuf
Get the storage path for a canonical path.
Sourcepub fn get_canonical_path(&self, storage_path: &str) -> String
pub fn get_canonical_path(&self, storage_path: &str) -> String
Get the canonical path from a storage path.
Sourcepub async fn handle_crdt_state(&self, state: &[u8]) -> Result<usize>
pub async fn handle_crdt_state(&self, state: &[u8]) -> Result<usize>
Handle the CrdtState message from the server’s handshake protocol.
This is called after the client has downloaded all files (via HTTP or
batch request) and sent the FilesReady message. The server then
sends the full CRDT state which is applied to the workspace.
Important: This method is designed for new clients with empty workspaces.
When the local workspace is empty, applying the server’s full state via
apply_update works correctly without tombstoning issues. The handshake
protocol ensures files are downloaded BEFORE this state is applied, so
the CRDT state and filesystem are consistent.
§Arguments
state- The full CRDT state as bytes (Y-update v1 encoded)
§Returns
The number of files in the workspace after applying the state
Sourcepub fn is_workspace_empty(&self) -> bool
pub fn is_workspace_empty(&self) -> bool
Check if the workspace CRDT is empty (no files).
Used to determine if this is a “new client” that needs the handshake protocol to prevent CRDT corruption.
Sourcepub fn rebuild_uuid_maps(&self)
pub fn rebuild_uuid_maps(&self)
Rebuild the bidirectional UUID ↔ path mapping from the workspace CRDT.
Should be called after any workspace CRDT mutation (handle_workspace_message, handle_crdt_state, or local metadata changes).
Sourcepub fn resolve_path(&self, uuid: &str) -> Option<String>
pub fn resolve_path(&self, uuid: &str) -> Option<String>
Resolve a UUID to its current filesystem path.
Sourcepub fn resolve_uuid(&self, path: &str) -> Option<String>
pub fn resolve_uuid(&self, path: &str) -> Option<String>
Resolve a filesystem path to its UUID in the workspace CRDT.
Sourcepub fn get_all_file_uuids(&self) -> Vec<String>
pub fn get_all_file_uuids(&self) -> Vec<String>
Get all active file UUIDs in the workspace CRDT.