aboutsummaryrefslogtreecommitdiffstats
path: root/src/document
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-10-31 13:28:55 +0100
committertv <tv@krebsco.de>2026-10-31 13:28:55 +0100
commit1d0b1646c5cbb9cb177369189539a2bf0d132ba8 (patch)
tree4fd36872fdddda3c315665c0b9050eaae3c586de /src/document
parent36ffffb89b1dfeca0163b6ecd9c46239aca6465e (diff)
one function for changed paths
Diffstat (limited to 'src/document')
-rw-r--r--src/document/store.rs61
1 files changed, 61 insertions, 0 deletions
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<Vec<String>>,
}
+/// 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<String>,
+ 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<Changed> {
+ 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<String> {
+ 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)