aboutsummaryrefslogtreecommitdiffstats
path: root/src/remake/kind.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/remake/kind.rs')
-rw-r--r--src/remake/kind.rs102
1 files changed, 54 insertions, 48 deletions
diff --git a/src/remake/kind.rs b/src/remake/kind.rs
index aa9e978..03f2ef7 100644
--- a/src/remake/kind.rs
+++ b/src/remake/kind.rs
@@ -19,8 +19,8 @@
use std::path::Path;
+use crate::document::{Document, Transaction};
use anyhow::{Context, anyhow, bail};
-use automerge::AutoCommit;
use base64::engine::{Engine, general_purpose::STANDARD as BASE64};
use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue;
@@ -69,9 +69,9 @@ pub(crate) trait RemakeKind: Send + Sync + 'static {
/// must not write under `_continuum` (anything there is replaced).
fn build(
&self,
- old_at_base: &AutoCommit,
+ old_at_base: &Document,
content: Option<&[u8]>,
- target: &mut AutoCommit,
+ target: &mut Transaction<'_>,
) -> Result<(), StoreError>;
/// Every node: where a user path written after the base goes.
@@ -105,7 +105,7 @@ pub(crate) fn by_name(name: &str) -> Option<&'static dyn RemakeKind> {
pub(crate) fn remake_genesis(
spec: &str,
files: &Files,
- target: &mut AutoCommit,
+ target: &mut Document,
) -> anyhow::Result<()> {
let (name, path) = match spec.split_once(':') {
Some((name, path)) => (name, Some(Path::new(path))),
@@ -135,16 +135,16 @@ pub(crate) fn remake_genesis(
let prepared = kind
.propose(&request)
.map_err(|reason| anyhow!("{name}: {reason}"))?;
- kind.build(&AutoCommit::new(), prepared.content.as_deref(), target)?;
- target.commit();
+ let mut transaction = target.transaction();
+ kind.build(
+ &Document::new(),
+ prepared.content.as_deref(),
+ &mut transaction,
+ )?;
+ transaction.commit();
Ok(())
}
-/// Paths owned by the protocol rather than by any kind.
-pub(crate) fn is_reserved(path: &[String]) -> bool {
- meta::is_protocol(path)
-}
-
/// Compaction: the new document has the old one's content at the base, without its
/// history. Writes keep their paths.
pub(crate) struct Compact;
@@ -168,11 +168,11 @@ impl RemakeKind for Compact {
fn build(
&self,
- old_at_base: &AutoCommit,
+ old_at_base: &Document,
_content: Option<&[u8]>,
- target: &mut AutoCommit,
+ target: &mut Transaction<'_>,
) -> Result<(), StoreError> {
- store::materialize_into(old_at_base, target, &is_reserved)
+ store::materialize_into(old_at_base, target, &meta::is_protocol)
}
fn translate(&self, _path: &[String], _translation: &JsonValue) -> Translation {
@@ -272,9 +272,9 @@ impl RemakeKind for Replace {
fn build(
&self,
- _old_at_base: &AutoCommit,
+ _old_at_base: &Document,
content: Option<&[u8]>,
- target: &mut AutoCommit,
+ target: &mut Transaction<'_>,
) -> Result<(), StoreError> {
let content = content.ok_or_else(|| anyhow::anyhow!("replace without content"))?;
let document: JsonValue = serde_json::from_slice(content)
@@ -311,7 +311,7 @@ fn prepare_rules(specs: Vec<RuleSpec>) -> Result<JsonValue, String> {
));
}
};
- if is_reserved(&from) || to.as_deref().is_some_and(is_reserved) {
+ if meta::is_protocol(&from) || to.as_deref().is_some_and(meta::is_protocol) {
return Err(format!(
"rules must not touch the reserved key {:?}",
meta::ROOT
@@ -370,11 +370,11 @@ struct ImportSpec {
}
/// The document in a request (base64 of a saved Automerge document).
-fn load_encoded(encoded: &str) -> Result<AutoCommit, String> {
+fn load_encoded(encoded: &str) -> Result<Document, String> {
let bytes = BASE64
.decode(encoded.trim())
.map_err(|error| format!("the document is not valid base64: {error}"))?;
- AutoCommit::load(&bytes)
+ Document::load(&bytes)
.map_err(|error| format!("the document is not an Automerge document: {error}"))
}
@@ -391,9 +391,11 @@ impl RemakeKind for Import {
// Keep only what goes into the new epoch: the merged view without protocol data
// and history. Usually much smaller than what was uploaded, and it is what every
// voter downloads.
- let mut content = AutoCommit::new();
- store::materialize_into(&imported, &mut content, &is_reserved)
+ let mut content = Document::new();
+ let mut transaction = content.transaction();
+ store::materialize_into(&imported, &mut transaction, &meta::is_protocol)
.map_err(|error| format!("the document can't be read: {error}"))?;
+ transaction.commit();
Ok(Prepared {
content: Some(content.save()),
@@ -403,14 +405,14 @@ impl RemakeKind for Import {
fn build(
&self,
- _old_at_base: &AutoCommit,
+ _old_at_base: &Document,
content: Option<&[u8]>,
- target: &mut AutoCommit,
+ target: &mut Transaction<'_>,
) -> Result<(), StoreError> {
let content = content.ok_or_else(|| anyhow::anyhow!("import without content"))?;
- let imported = AutoCommit::load(content)?;
+ let imported = Document::load(content)?;
// Deterministic: the same bytes give the same view, copied in a fixed order.
- store::materialize_into(&imported, target, &is_reserved)
+ store::materialize_into(&imported, target, &meta::is_protocol)
}
fn translate(&self, path: &[String], translation: &JsonValue) -> Translation {
@@ -439,7 +441,7 @@ impl Translator {
/// Where `path` ends up after all steps. Reserved paths always stay put.
pub(crate) fn translate(&self, path: &[String]) -> Translation {
- if is_reserved(path) {
+ if meta::is_protocol(path) {
return Translation::Same;
}
let mut current = path.to_vec();
@@ -478,9 +480,9 @@ mod tests {
}
fn build(
&self,
- _old: &AutoCommit,
+ _old: &Document,
_content: Option<&[u8]>,
- _target: &mut AutoCommit,
+ _target: &mut Transaction<'_>,
) -> Result<(), StoreError> {
Ok(())
}
@@ -596,17 +598,20 @@ mod tests {
#[test]
fn import_keeps_types_and_drops_protocol_data() {
+ use crate::document::testing::{change, try_change};
use automerge::{ObjType, ROOT, ReadDoc, transaction::Transactable};
use serde_json::json;
- let mut source = AutoCommit::new();
- let text = source.put_object(ROOT, "notes", ObjType::Text).unwrap();
- source.splice_text(&text, 0, 0, "hello").unwrap();
- source
- .put(ROOT, "hits", automerge::ScalarValue::counter(3))
- .unwrap();
- let meta = source.put_object(ROOT, meta::ROOT, ObjType::Map).unwrap();
- source.put(&meta, "members", "elsewhere").unwrap();
+ let mut source = Document::new();
+ change(&mut source, |source| {
+ let text = source.put_object(ROOT, "notes", ObjType::Text).unwrap();
+ source.splice_text(&text, 0, 0, "hello").unwrap();
+ source
+ .put(ROOT, "hits", automerge::ScalarValue::counter(3))
+ .unwrap();
+ let meta = source.put_object(ROOT, meta::ROOT, ObjType::Map).unwrap();
+ source.put(&meta, "members", "elsewhere").unwrap();
+ });
let encoded = BASE64.encode(source.save());
assert!(
@@ -624,15 +629,15 @@ mod tests {
.propose(&json!({ "document": encoded, "rules": [{ "from": "", "to": "" }] }))
.unwrap();
let build = || {
- let mut target = AutoCommit::new().with_actor(automerge::ActorId::from(&b"fixed"[..]));
- Import
- .build(&AutoCommit::new(), prepared.content.as_deref(), &mut target)
- .unwrap();
- target.commit_with(automerge::transaction::CommitOptions::default().with_time(0));
+ let mut target = Document::new().with_actor(automerge::ActorId::from(&b"fixed"[..]));
+ try_change(&mut target, |target| {
+ Import.build(&Document::new(), prepared.content.as_deref(), target)
+ })
+ .unwrap();
target
};
- let mut a = build();
- let mut b = build();
+ let a = build();
+ let b = build();
assert_eq!(a.get_heads(), b.get_heads());
assert!(a.get(ROOT, meta::ROOT).unwrap().is_none());
@@ -653,14 +658,15 @@ mod tests {
#[test]
fn replace_builds_the_document_deterministically() {
+ use crate::document::testing::try_change;
use serde_json::json;
let build = |document: serde_json::Value| {
let prepared = replace(json!({ "document": document })).unwrap();
- let mut target = AutoCommit::new().with_actor(automerge::ActorId::from(&b"fixed"[..]));
- Replace
- .build(&AutoCommit::new(), prepared.content.as_deref(), &mut target)
- .unwrap();
- target.commit_with(automerge::transaction::CommitOptions::default().with_time(0));
+ let mut target = Document::new().with_actor(automerge::ActorId::from(&b"fixed"[..]));
+ try_change(&mut target, |target| {
+ Replace.build(&Document::new(), prepared.content.as_deref(), target)
+ })
+ .unwrap();
(
target.get_heads(),
store::read_path(&target, &[]).unwrap().unwrap(),