Skip to main content

blockworx_store/
lib.rs

1//! The durable store: the `.bwx` container, the rev files that *are* the
2//! document, and the manifest that names them.
3//! Rationale: `docs/json-format.md`; `docs/log-vs-snapshot.md`.
4//!
5//! ```text
6//! Commit ──▶ Document ──▶ revs/{rev}.json.gz ──▶ one row of manifest.jsonl
7//! ```
8//!
9//! The rev files are authoritative and everything beside them is derived,
10//! so this crate is written for one property: what the editor holds and
11//! what the files hold agree at every point a call can return. A rev lands
12//! and is fsync'd before the row that names it; loads verify every chain
13//! link and the head rev's own bytes before the document is shown; and the
14//! container gives up its lock rather than write into a history it has
15//! already lost track of.
16//!
17//! All of it is target-independent: the container's layout is written once
18//! over a [`Storage`](storage::Storage) that says only where the bytes go,
19//! so the browser reads the same rows and revs out of its origin storage
20//! that a desktop reads out of a directory, and a session with no files at
21//! all stands its revs in memory.
22
23pub mod assets;
24pub mod doc;
25pub mod document_file;
26pub mod history;
27pub mod manifest;
28pub mod record;
29pub mod revs;
30pub mod stamp;
31pub mod tags;
32pub mod worked;
33
34pub mod atomic;
35pub mod container;
36pub mod dump;
37pub mod handle;
38pub mod lock;
39pub mod naming;
40pub mod prefix;
41pub mod recent;
42pub mod storage;
43pub mod transfer;
44
45/// Carrying a container written when revs were zstd over to gzip. Native
46/// only, and the last thing in the tree that names zstd.
47#[cfg(not(target_arch = "wasm32"))]
48pub mod migrate;
49
50/// What a *path* names, and the container doors that take one. The one
51/// module here that is still native by nature: a browser has no
52/// filesystem to point at, and reaches its containers by name instead.
53#[cfg(not(target_arch = "wasm32"))]
54pub mod file;
55
56/// Builders and a scratch directory the app's suites share with this crate's,
57/// so both drive the store through one set of fixtures.
58///
59/// A failed fold in a fixture *is* the assertion, so the panic lints are off
60/// here as they are inside a `#[cfg(test)]` module.
61#[cfg(any(test, feature = "test-support"))]
62#[expect(clippy::expect_used, clippy::panic, clippy::missing_panics_doc)]
63pub mod fixture;
64#[cfg(any(test, feature = "test-support"))]
65#[expect(clippy::unwrap_used, clippy::missing_panics_doc)]
66pub mod temp;
67
68#[cfg(test)]
69mod tests;
70
71use blockworx_doc::{document::FoldError, rev::Rev, trail::UndoRefusal};
72
73/// Why a write did not happen. Target-independent because the editor's
74/// document handle ([`doc::Doc`]) reports refusals in one spelling
75/// whether it is holding a container or an in-process session, and the web
76/// build has only the latter.
77#[derive(Debug, thiserror::Error)]
78pub enum Refusal {
79    #[error("this diagram is open read-only")]
80    ReadOnly,
81    #[error("this session has no diagram on disk to write")]
82    Detached,
83    #[error(transparent)]
84    Fold(#[from] FoldError),
85    #[error(transparent)]
86    Step(#[from] UndoRefusal),
87    #[error("this history holds no rev {}", .0.get())]
88    NoSuchRev(Rev),
89    #[error("the document at rev {} could not be read back: {why}", .at.get())]
90    Unreachable { at: Rev, why: String },
91    #[error("the row could not be appended: {0}")]
92    Append(std::io::Error),
93    #[error("the diagram could not be renamed: {0}")]
94    Rename(std::io::Error),
95    #[error("the diagram file could not be written: {0}")]
96    Projection(std::io::Error),
97}