aboutsummaryrefslogtreecommitdiffstats
path: root/src/document/store.rs
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-10-31 19:51:52 +0100
committertv <tv@krebsco.de>2026-10-31 19:51:52 +0100
commit8d7adfbdb7798cca9d9783761f290e3bd85d9a3a (patch)
tree9efd09632fddfab97b6ff93b0f153c0a8de35c35 /src/document/store.rs
parente4f801db5d150fb1bf5540d9ba00f491a6129a5c (diff)
store function accept any document
Diffstat (limited to 'src/document/store.rs')
-rw-r--r--src/document/store.rs92
1 files changed, 50 insertions, 42 deletions
diff --git a/src/document/store.rs b/src/document/store.rs
index c6c428c..7d54626 100644
--- a/src/document/store.rs
+++ b/src/document/store.rs
@@ -118,7 +118,7 @@ enum Resolved {
/// All concurrent values of `key` across a merged set of parent maps, newest first.
fn candidates(
- doc: &AutoCommit,
+ doc: &impl ReadDoc,
parents: &[ObjId],
prop: impl Into<Prop> + Clone,
) -> Result<Vec<(Value<'static>, ObjId)>, StoreError> {
@@ -133,7 +133,7 @@ fn candidates(
}
fn resolve(
- doc: &AutoCommit,
+ doc: &impl ReadDoc,
parents: &[ObjId],
key: &str,
log: Option<&mut ConflictLog>,
@@ -143,7 +143,7 @@ fn resolve(
/// The item at `index` of `list` (concurrent writes there resolved as for a key).
fn resolve_item(
- doc: &AutoCommit,
+ doc: &impl ReadDoc,
list: &ObjId,
index: usize,
log: Option<&mut ConflictLog>,
@@ -163,7 +163,7 @@ fn resolve_item(
/// What `segment` names within `current`: a key of merged maps, or the item of a list
/// at the index it spells (`list_index`); None for anything else, and in a scalar.
fn step(
- doc: &AutoCommit,
+ doc: &impl ReadDoc,
current: &Resolved,
segment: &str,
log: Option<&mut ConflictLog>,
@@ -197,7 +197,7 @@ fn from_the_end(segment: &str) -> Option<usize> {
/// None if it has none. An index before the start names nothing (`NotFound`). Only
/// segments within a list count: in a map, `-1` is a key like any other.
pub(crate) fn positive_path(
- doc: &AutoCommit,
+ doc: &impl ReadDoc,
path: &[String],
insertion: bool,
) -> Result<Option<Vec<String>>, StoreError> {
@@ -228,7 +228,7 @@ pub(crate) fn positive_path(
/// Resolves `key` (a map key or a list index) in `parents`; `name` is how conflict
/// reports name it.
fn resolve_prop(
- doc: &AutoCommit,
+ doc: &impl ReadDoc,
parents: &[ObjId],
prop: impl Into<Prop> + Clone,
name: &str,
@@ -261,12 +261,12 @@ fn resolve_prop(
}
}
-fn merged_keys(doc: &AutoCommit, maps: &[ObjId]) -> BTreeSet<String> {
+fn merged_keys(doc: &impl ReadDoc, maps: &[ObjId]) -> BTreeSet<String> {
maps.iter().flat_map(|map| doc.keys(map)).collect()
}
fn resolved_to_json(
- doc: &AutoCommit,
+ doc: &impl ReadDoc,
resolved: &Resolved,
mut log: Option<&mut ConflictLog>,
) -> Result<JsonValue, StoreError> {
@@ -316,7 +316,7 @@ fn resolved_to_json(
}
fn object_to_json(
- doc: &AutoCommit,
+ doc: &impl ReadDoc,
id: &ObjId,
mut log: Option<&mut ConflictLog>,
) -> Result<JsonValue, StoreError> {
@@ -340,7 +340,7 @@ fn object_to_json(
}
}
-fn delete_in_all(doc: &mut AutoCommit, maps: &[ObjId], key: &str) -> Result<(), StoreError> {
+fn delete_in_all(doc: &mut impl Transactable, maps: &[ObjId], key: &str) -> Result<(), StoreError> {
for map in maps {
if doc.get(map, key)?.is_some() {
doc.delete(map, key)?;
@@ -350,7 +350,7 @@ fn delete_in_all(doc: &mut AutoCommit, maps: &[ObjId], key: &str) -> Result<(),
}
fn read_path_inner(
- doc: &AutoCommit,
+ doc: &impl ReadDoc,
path: &[String],
mut log: Option<&mut ConflictLog>,
) -> Result<Option<JsonValue>, StoreError> {
@@ -362,14 +362,14 @@ fn read_path_inner(
/// Reads a path; the empty path is the whole store.
pub(crate) fn read_path(
- doc: &AutoCommit,
+ doc: &impl ReadDoc,
path: &[String],
) -> Result<Option<JsonValue>, StoreError> {
read_path_inner(doc, path, None)
}
pub(crate) fn read_path_with_conflicts(
- doc: &AutoCommit,
+ doc: &impl ReadDoc,
path: &[String],
) -> Result<Option<(JsonValue, Vec<ConflictReport>)>, StoreError> {
let mut log = ConflictLog {
@@ -383,7 +383,7 @@ pub(crate) fn read_path_with_conflicts(
/// For streams: the value, plus whether the path or subtree involves any conflict.
/// Reports are collected without values, so this costs little more than read_path.
pub(crate) fn read_path_traced(
- doc: &AutoCommit,
+ doc: &impl ReadDoc,
path: &[String],
) -> Result<(Option<JsonValue>, bool), StoreError> {
let mut log = ConflictLog::default();
@@ -401,7 +401,7 @@ const SHALLOW_INLINE: usize = 256;
/// scalar; a map's has its keys' summaries too, under `entries`. Concurrent values
/// resolved as a read resolves them; conflicts not reported.
pub(crate) fn read_shallow(
- doc: &AutoCommit,
+ doc: &impl ReadDoc,
path: &[String],
) -> Result<Option<JsonValue>, StoreError> {
let Some(resolved) = resolve_through(doc, path)? else {
@@ -412,14 +412,14 @@ pub(crate) fn read_shallow(
/// The resolved value at `path` (the empty path is the root map), through maps and
/// lists (`step`).
-fn resolve_through(doc: &AutoCommit, path: &[String]) -> Result<Option<Resolved>, StoreError> {
+fn resolve_through(doc: &impl ReadDoc, path: &[String]) -> Result<Option<Resolved>, StoreError> {
resolve_logged(doc, path, None)
}
/// `resolve_through`, noting the conflicts on the way in `log` (as ancestors, but the
/// last segment's: the requested value's), its path left at `path`.
fn resolve_logged(
- doc: &AutoCommit,
+ doc: &impl ReadDoc,
path: &[String],
mut log: Option<&mut ConflictLog>,
) -> Result<Option<Resolved>, StoreError> {
@@ -445,7 +445,11 @@ fn resolve_logged(
/// A summary of `resolved` (`read_shallow`); with `entries`, a map's keys' too, or a
/// list's items' (by index).
-fn summary(doc: &AutoCommit, resolved: &Resolved, entries: bool) -> Result<JsonValue, StoreError> {
+fn summary(
+ doc: &impl ReadDoc,
+ resolved: &Resolved,
+ entries: bool,
+) -> Result<JsonValue, StoreError> {
let mut out = serde_json::Map::new();
match resolved {
Resolved::Maps(maps) => {
@@ -508,7 +512,7 @@ fn summary(doc: &AutoCommit, resolved: &Resolved, entries: bool) -> Result<JsonV
}
/// Keys of the merged map at `path`; empty if it doesn't exist or isn't a map.
-pub(crate) fn keys_at(doc: &AutoCommit, path: &[String]) -> Result<Vec<String>, StoreError> {
+pub(crate) fn keys_at(doc: &impl ReadDoc, path: &[String]) -> Result<Vec<String>, StoreError> {
Ok(match resolve_through(doc, path)? {
Some(Resolved::Maps(maps)) => merged_keys(doc, &maps).into_iter().collect(),
_ => Vec::new(),
@@ -518,7 +522,7 @@ pub(crate) fn keys_at(doc: &AutoCommit, path: &[String]) -> Result<Vec<String>,
/// Every concurrent value at `path`, newest first; empty if there is none. Where
/// `read_path` picks one (or merges concurrent maps into one), this keeps them apart.
pub(crate) fn read_candidates(
- doc: &AutoCommit,
+ doc: &impl ReadDoc,
path: &[String],
) -> Result<Vec<JsonValue>, StoreError> {
let (leaf, parents) = path.split_last().ok_or(StoreError::NotFound)?;
@@ -538,7 +542,7 @@ pub(crate) fn read_candidates(
/// the segment is an index (`list_index`): an item there is replaced, one at the end
/// appended; past the end, or anything but an index, names nothing (`NotFound`).
pub(crate) fn write_path(
- doc: &mut AutoCommit,
+ doc: &mut impl Transactable,
path: &[String],
value: &JsonValue,
) -> Result<(), StoreError> {
@@ -575,7 +579,7 @@ enum Parent {
/// The parent of a write at `parents` + one more segment: missing maps are created on
/// the way; through a list only by an index of an item it has. A scalar or text on the
/// way is a `Conflict`.
-fn parent_for_write(doc: &mut AutoCommit, parents: &[String]) -> Result<Parent, StoreError> {
+fn parent_for_write(doc: &mut impl Transactable, parents: &[String]) -> Result<Parent, StoreError> {
let mut current = Parent::Maps(vec![automerge::ROOT]);
for segment in parents {
let next = match &current {
@@ -607,7 +611,7 @@ fn parent_for_write(doc: &mut AutoCommit, parents: &[String]) -> Result<Parent,
/// `PastTheEnd`; a parent that isn't a list `NotAList`; anything but an index there, or
/// no parent, `NotFound`.
pub(crate) fn insert_path(
- doc: &mut AutoCommit,
+ doc: &mut impl Transactable,
path: &[String],
value: &JsonValue,
) -> Result<(), StoreError> {
@@ -630,8 +634,8 @@ pub(crate) fn insert_path(
/// copy (see `materialize_into`) if it exists, a delete otherwise. Replaces the whole
/// subtree at `path`; the path must not be empty.
pub(crate) fn copy_path(
- src: &AutoCommit,
- dst: &mut AutoCommit,
+ src: &impl ReadDoc,
+ dst: &mut impl Transactable,
path: &[String],
) -> Result<(), StoreError> {
copy_path_to(src, path, dst, path)
@@ -639,9 +643,9 @@ pub(crate) fn copy_path(
/// Like [`copy_path`], but puts what `src` holds at `from` at `to` in `dst`.
pub(crate) fn copy_path_to(
- src: &AutoCommit,
+ src: &impl ReadDoc,
from: &[String],
- dst: &mut AutoCommit,
+ dst: &mut impl Transactable,
to: &[String],
) -> Result<(), StoreError> {
let (leaf, parents) = to.split_last().ok_or(StoreError::NotFound)?;
@@ -675,7 +679,7 @@ pub(crate) fn copy_path_to(
/// (`list_index`; the items after it move down one). With `prune`, the maps above it
/// that it leaves empty go too, up to (not including) the root or a list.
pub(crate) fn delete_path(
- doc: &mut AutoCommit,
+ doc: &mut impl Transactable,
path: &[String],
prune: bool,
) -> Result<(), StoreError> {
@@ -739,8 +743,8 @@ pub(crate) fn delete_path(
/// `skip` sees the path of every map entry and list element (list indices as decimal
/// strings); returning true leaves that subtree out.
pub(crate) fn materialize_into(
- src: &AutoCommit,
- dst: &mut AutoCommit,
+ src: &impl ReadDoc,
+ dst: &mut impl Transactable,
skip: &dyn Fn(&[String]) -> bool,
) -> Result<(), StoreError> {
let mut path = Vec::new();
@@ -755,9 +759,9 @@ pub(crate) fn materialize_into(
}
fn copy_maps(
- src: &AutoCommit,
+ src: &impl ReadDoc,
maps: &[ObjId],
- dst: &mut AutoCommit,
+ dst: &mut impl Transactable,
target: &ObjId,
path: &mut Vec<String>,
skip: &dyn Fn(&[String]) -> bool,
@@ -776,9 +780,9 @@ fn copy_maps(
}
fn copy_resolved(
- src: &AutoCommit,
+ src: &impl ReadDoc,
resolved: &Resolved,
- dst: &mut AutoCommit,
+ dst: &mut impl Transactable,
parent: &ObjId,
slot: Slot,
path: &mut Vec<String>,
@@ -806,10 +810,10 @@ fn copy_resolved(
#[allow(clippy::too_many_arguments)]
fn copy_object(
- src: &AutoCommit,
+ src: &impl ReadDoc,
obj_type: ObjType,
id: &ObjId,
- dst: &mut AutoCommit,
+ dst: &mut impl Transactable,
parent: &ObjId,
slot: Slot,
path: &mut Vec<String>,
@@ -863,7 +867,7 @@ enum Slot<'a> {
}
fn put_object(
- doc: &mut AutoCommit,
+ doc: &mut impl Transactable,
parent: &ObjId,
slot: Slot,
obj_type: ObjType,
@@ -875,7 +879,7 @@ fn put_object(
}
fn put_scalar(
- doc: &mut AutoCommit,
+ doc: &mut impl Transactable,
parent: &ObjId,
slot: Slot,
value: impl Into<ScalarValue>,
@@ -888,7 +892,7 @@ fn put_scalar(
}
fn write_value(
- doc: &mut AutoCommit,
+ doc: &mut impl Transactable,
parent: &ObjId,
slot: Slot,
value: &JsonValue,
@@ -926,7 +930,7 @@ fn write_value(
}
fn json_to_automerge(
- doc: &mut AutoCommit,
+ doc: &mut impl Transactable,
parent: &ObjId,
key: &str,
value: &JsonValue,
@@ -997,7 +1001,11 @@ fn op_id_string(id: &ObjId) -> String {
}
}
-fn value_to_json(doc: &AutoCommit, value: &Value<'_>, id: &ObjId) -> Result<JsonValue, StoreError> {
+fn value_to_json(
+ doc: &impl ReadDoc,
+ value: &Value<'_>,
+ id: &ObjId,
+) -> Result<JsonValue, StoreError> {
match value {
Value::Scalar(scalar) => Ok(scalar_to_json(scalar.clone().into_owned())?),
Value::Object(_) => object_to_json(doc, id, None),
@@ -1007,7 +1015,7 @@ fn value_to_json(doc: &AutoCommit, value: &Value<'_>, id: &ObjId) -> Result<Json
impl ConflictLog {
fn record(
&mut self,
- doc: &AutoCommit,
+ doc: &impl ReadDoc,
key: &str,
all: &[(Value<'static>, ObjId)],
merged: bool,