1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
//! 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 `AutoCommit`), its changes and their hashes, and its writers (actors).
/// Only `document` and `remake` use Automerge itself (`architecture.md`, The document).
pub(crate) use automerge::{ActorId, AutoCommit as Document, Change, ChangeHash};
/// 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: &mut 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: &mut 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 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)]
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 `remake`; the rest of the node 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 allowed = [src.join("document"), src.join("remake")];
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")
);
}
}
|