From a3d27a7e20f0279d34bb683782947658c48d8300 Mon Sep 17 00:00:00 2001 From: tv Date: Sat, 31 Oct 2026 13:28:55 +0100 Subject: one function for changed paths --- src/document/store.rs | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) (limited to 'src/document') diff --git a/src/document/store.rs b/src/document/store.rs index eeebda8..dff0cfb 100644 --- a/src/document/store.rs +++ b/src/document/store.rs @@ -16,6 +16,67 @@ pub(crate) struct ChangeSet { pub(crate) paths: Vec>, } +/// How `changed` names a change inside a list. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) enum InLists { + /// By the item's index: `servers/0/name` (the change publisher). + Index, + /// By the list: `servers` (remakes replay a list whole). + Whole, +} + +/// A store path changes touched, and whether concurrent writes left a conflict there. +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) struct Changed { + pub(crate) path: Vec, + pub(crate) conflict: bool, +} + +/// The store paths the changes from `before` to `after` touched, as the document +/// reports them (a path may come more than once). A map entry by its key, a counter +/// incremented in a map by its key, and list and text edits by the containing object; +/// inside a list as `lists` says. +pub(crate) fn changed( + doc: &mut AutoCommit, + before: &[automerge::ChangeHash], + after: &[automerge::ChangeHash], + lists: InLists, +) -> Vec { + doc.diff(before, after) + .iter() + .map(|patch| Changed { + path: patch_path(patch, lists), + conflict: matches!( + patch.action, + automerge::PatchAction::PutMap { conflict: true, .. } + ), + }) + .collect() +} + +fn patch_path(patch: &automerge::Patch, lists: InLists) -> Vec { + use automerge::PatchAction; + let mut path = Vec::new(); + for (_, prop) in &patch.path { + match (prop, lists) { + (Prop::Map(key), _) => path.push(key.clone()), + (Prop::Seq(index), InLists::Index) => path.push(index.to_string()), + (Prop::Seq(_), InLists::Whole) => return path, + } + } + match &patch.action { + PatchAction::PutMap { key, .. } + | PatchAction::DeleteMap { key, .. } + | PatchAction::Increment { + prop: Prop::Map(key), + .. + } => path.push(key.clone()), + // List and text edits: the containing object. + _ => {} + } + path +} + /// Whether one path is within the other (a change at either touches both). pub(crate) fn paths_related(a: &[String], b: &[String]) -> bool { a.starts_with(b) || b.starts_with(a) -- cgit v1.3.1