diff options
Diffstat (limited to 'src/remake/kind.rs')
| -rw-r--r-- | src/remake/kind.rs | 102 |
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(), |
