diff options
| author | tv <tv@krebsco.de> | 2026-10-20 00:51:39 +0200 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-10-20 00:51:39 +0200 |
| commit | a30499ffedb76dc08ba7e9c9b2ed4eb3f921a5df (patch) | |
| tree | b783cd8dac2d73c768586a08356248acfeb32c3b /src/store.rs | |
| parent | ae34ebb3b70d529e93ea423f50b46a4ade15259b (diff) | |
add epoch switch
Diffstat (limited to 'src/store.rs')
| -rw-r--r-- | src/store.rs | 63 |
1 files changed, 57 insertions, 6 deletions
diff --git a/src/store.rs b/src/store.rs index 1f1398e..1815635 100644 --- a/src/store.rs +++ b/src/store.rs @@ -250,9 +250,19 @@ pub fn write_path( value: &JsonValue, ) -> Result<(), StoreError> { let (leaf, parents) = path.split_last().ok_or(StoreError::NotFound)?; + let maps = ensure_maps(doc, parents)?; + // Replace semantics: clear the key in every merged parent, or an old map + // would merge into the new value. + delete_in_all(doc, &maps, leaf)?; + json_to_automerge(doc, &maps[0], leaf, value)?; + Ok(()) +} + +/// The merged maps at `path`, creating missing maps on the way. +fn ensure_maps(doc: &mut AutoCommit, path: &[String]) -> Result<Vec<ObjId>, StoreError> { let mut maps = vec![automerge::ROOT]; - for segment in parents { + for segment in path { maps = match resolve(doc, &maps, segment, None)? { Some(Resolved::Maps(next)) => next, Some(Resolved::Other(..)) => return Err(StoreError::Conflict), @@ -260,12 +270,53 @@ pub fn write_path( None => vec![doc.put_object(&maps[0], segment.as_str(), ObjType::Map)?], }; } + Ok(maps) +} - // Replace semantics: clear the key in every merged parent, or an old map - // would merge into the new value. - delete_in_all(doc, &maps, leaf)?; - json_to_automerge(doc, &maps[0], leaf, value)?; - Ok(()) +/// The resolved value at `path` (the empty path is the root map). +fn resolve_at(doc: &AutoCommit, path: &[String]) -> Result<Option<Resolved>, StoreError> { + let mut current = Resolved::Maps(vec![automerge::ROOT]); + for segment in path { + let Resolved::Maps(maps) = ¤t else { + return Ok(None); + }; + match resolve(doc, maps, segment, None)? { + Some(next) => current = next, + None => return Ok(None), + } + } + Ok(Some(current)) +} + +/// Makes `dst` hold at `path` what `src`'s merged view holds there: a type-preserving +/// 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, + path: &[String], +) -> Result<(), StoreError> { + let (leaf, parents) = path.split_last().ok_or(StoreError::NotFound)?; + + let Some(resolved) = resolve_at(src, path)? else { + return match delete_path(dst, path, false) { + Ok(()) | Err(StoreError::NotFound) => Ok(()), + Err(error) => Err(error), + }; + }; + + let maps = ensure_maps(dst, parents)?; + delete_in_all(dst, &maps, leaf)?; + let mut trace = path.to_vec(); + copy_resolved( + src, + &resolved, + dst, + &maps[0], + Slot::Key(leaf), + &mut trace, + &|_| false, + ) } /// Deletes the value at `path`, optionally pruning ancestors that become empty. |
