diff options
Diffstat (limited to 'src/document/mod.rs')
| -rw-r--r-- | src/document/mod.rs | 96 |
1 files changed, 96 insertions, 0 deletions
diff --git a/src/document/mod.rs b/src/document/mod.rs index b86d1cc..21050ea 100644 --- a/src/document/mod.rs +++ b/src/document/mod.rs @@ -1,8 +1,104 @@ //! The replicated document: the replica with its epoch, and the store semantics //! (merged-view paths over JSON) the HTTP API and remakes work with. +/// The engine's types, under the names the rest of the crate uses: the document +/// (Automerge's `Automerge`), its changes and their hashes, and its writers (actors); +/// a write to it (`Transaction`: one change, committed or dropped whole), and what can +/// be read like a document (`ReadDoc`: the document, or a write in progress) and written +/// like one (`Transactable`). +/// Only `document` and `remake` use Automerge itself (`architecture.md`, The document). +pub(crate) use automerge::transaction::{Transactable, Transaction}; +pub(crate) use automerge::{ActorId, Automerge as Document, Change, ChangeHash, ReadDoc}; + +/// The whole document as bytes, as the engine saves it: a backup, the document +/// file's full save, the size a joining node downloads. +pub(crate) fn save(doc: &Document) -> Vec<u8> { + doc.save() +} + +/// The changes after `since` as bytes that, appended to a save, load with it (the +/// document file's appends). +pub(crate) fn save_since(doc: &Document, since: &[ChangeHash]) -> Vec<u8> { + doc.save_after(since) +} + +/// A saved document (`save`), loaded to write to under a writer ID of its own: one ID +/// must never write in two places, and a save may be loaded on several nodes, or again +/// after the node that saved it wrote on. +pub(crate) fn load(bytes: &[u8]) -> anyhow::Result<Document> { + Ok(Document::load(bytes)?.with_actor(ActorId::random())) +} + +/// The actor (writer ID) `doc` writes under. +pub(crate) fn actor(doc: &Document) -> ActorId { + doc.get_actor().clone() +} + +pub(crate) mod at; +#[cfg(test)] +pub(crate) mod bench; +pub(crate) mod changes; pub(crate) mod file; pub(crate) mod lock; pub(crate) mod meta; pub(crate) mod replica; pub(crate) mod store; +pub(crate) mod sync; +#[cfg(test)] +pub(crate) mod testing; +pub(crate) mod typed; + +#[cfg(test)] +mod tests { + use std::path::{Path, PathBuf}; + + /// Every `.rs` file under `dir`. + fn sources(dir: &Path, out: &mut Vec<PathBuf>) { + for entry in std::fs::read_dir(dir).unwrap() { + let path = entry.unwrap().path(); + if path.is_dir() { + sources(&path, out); + } else if path.extension().is_some_and(|extension| extension == "rs") { + out.push(path); + } + } + } + + /// Automerge is used only in `document` and in what remakes do to documents + /// (snapshots, replaying late writes, catching up, the kinds, quarantined writes); + /// the rest of the node, remakes' coordination included, names its types as + /// `document` re-exports them and works through `document` (`architecture.md`, The + /// document). + #[test] + fn automerge_stays_behind_document() { + let src = Path::new(env!("CARGO_MANIFEST_DIR")).join("src"); + let mut allowed = vec![src.join("document")]; + for engine in ["snapshot", "delta", "catchup", "kind", "quarantine"] { + allowed.push(src.join("remake").join(format!("{engine}.rs"))); + } + let mut files = Vec::new(); + sources(&src, &mut files); + let mut found = Vec::new(); + for file in files + .iter() + .filter(|file| !allowed.iter().any(|allowed| file.starts_with(allowed))) + { + let text = std::fs::read_to_string(file).unwrap(); + for (number, line) in text.lines().enumerate() { + if line.contains("automerge::") || line.trim_start().starts_with("use automerge") { + found.push(format!( + "{}:{}: {}", + file.display(), + number + 1, + line.trim() + )); + } + } + } + assert!( + found.is_empty(), + "Automerge used outside `document`:\n{}", + found.join("\n") + ); + } +} |
