Skip to main content

dpm_store/
record.rs

1//! Records the store returns to adapters, available whether or not a SQLite backend is built.
2
3use crate::LineageError;
4use dpm_engine::Operation;
5use dpm_model::{LineageId, WorkspaceId};
6use serde::{Deserialize, Serialize};
7use std::path::Path;
8
9/// A committed operation with the workspace and writable history it was recorded in; mutation
10/// results and history entries return the same object, so a resent operation is answered with
11/// exactly what the first attempt returned.
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct RecordedOperation {
14    /// Identity, actor, timestamp, revision precondition and semantic command.
15    #[serde(flatten)]
16    pub operation: Operation,
17    /// Workspace the operation changed.
18    pub workspace_id: WorkspaceId,
19    /// Lineage of the store that committed it; a restored store keeps the lineages of the
20    /// operations it copied and records later ones under its own.
21    pub lineage_id: LineageId,
22}
23
24/// One immutable operation addressed by its local append order.
25#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct HistoryEntry {
27    /// Local database cursor, independent of wrapping domain revisions.
28    pub sequence: u64,
29    /// The recorded operation.
30    pub operation: RecordedOperation,
31}
32
33/// A bounded operation page read with a consistent authoritative revision.
34#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct HistoryPage {
36    /// Revision visible in the same SQLite transaction.
37    pub revision: u64,
38    /// Lineage the store continues, within which the revision is meaningful; absent only for a
39    /// page an adapter builds without a store.
40    pub lineage_id: Option<LineageId>,
41    /// Operations in ascending append order.
42    pub entries: Vec<HistoryEntry>,
43    /// Last returned sequence, suitable for the next request; unchanged for an empty page.
44    pub next_after_sequence: u64,
45}
46
47/// The lineage row of one store file.
48#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
49pub struct StoreLineage {
50    /// Writable history this file continues.
51    pub lineage_id: LineageId,
52    /// A backup archive, refused for writing until restored into a new store.
53    pub archived: bool,
54}
55
56impl StoreLineage {
57    /// Refuse a write to an archive, or to a lineage other than the one the caller observed.
58    pub fn check_writable(
59        &self,
60        expected: Option<LineageId>,
61        path: &Path,
62    ) -> Result<(), LineageError> {
63        if self.archived {
64            return Err(LineageError::Archived {
65                path: path.to_path_buf(),
66            });
67        }
68        match expected {
69            Some(expected) if expected != self.lineage_id => Err(LineageError::Mismatch {
70                expected,
71                actual: self.lineage_id,
72            }),
73            _ => Ok(()),
74        }
75    }
76}
77
78/// The authoritative revision of a store and the lineage it belongs to, read without decoding the
79/// plan, so clients can poll it for changes.
80#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
81pub struct StoreRevision {
82    /// Revision of the committed snapshot.
83    pub revision: u64,
84    /// Lineage within which the revision is meaningful.
85    pub lineage: StoreLineage,
86    /// Local sequence of the newest committed operation, zero for a store with none. It is read in
87    /// the same transaction as `revision`, so it is the exact cursor from which a subscriber to
88    /// the operation history sees every change after that snapshot and no earlier one.
89    pub history_head: u64,
90}