Skip to main content

nodedb_types/
namespace.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! Storage namespace identifiers for the blob KV store.
4//!
5//! Both SQLite (native) and OPFS (WASM) backends use the same namespace
6//! scheme to partition data by engine.
7
8use serde::{Deserialize, Serialize};
9
10/// Storage namespace. Each engine writes to its own namespace in the
11/// blob KV store, preventing key collisions.
12#[derive(
13    Debug,
14    Clone,
15    Copy,
16    PartialEq,
17    Eq,
18    Hash,
19    Serialize,
20    Deserialize,
21    rkyv::Archive,
22    rkyv::Serialize,
23    rkyv::Deserialize,
24)]
25#[repr(u8)]
26#[non_exhaustive]
27pub enum Namespace {
28    /// Database metadata: schema version, config, Shape subscriptions.
29    Meta = 0,
30    /// Vector engine: HNSW graph layers, vector data.
31    Vector = 1,
32    /// Graph engine: CSR arrays, node/label interning tables.
33    Graph = 2,
34    /// CRDT deltas: unsent mutations awaiting sync.
35    Crdt = 3,
36    /// Loro state snapshots: compacted CRDT state for fast cold-start.
37    LoroState = 4,
38    /// Spatial engine: R-tree checkpoints, geohash indexes.
39    Spatial = 5,
40    /// Strict document engine: Binary Tuple rows keyed by PK.
41    Strict = 6,
42    /// Columnar engine: compressed segments, delete bitmaps, segment metadata.
43    Columnar = 7,
44    /// KV engine: direct key-value storage (bypasses Loro CRDT).
45    /// Used when sync is disabled or for the local-only KV fast path.
46    Kv = 8,
47    /// Array engine: ND sparse arrays, catalog, manifests, segment bytes.
48    Array = 9,
49    /// Array CRDT op-log: append-only ops awaiting sync + GC.
50    ArrayOpLog = 10,
51    /// Array sync pending queue: ops waiting for transport delivery.
52    ArrayDelta = 11,
53    /// Full-text search engine: posting lists, doc-length maps, BM25 stats,
54    /// fieldnorm blobs, segment bytes, and surrogate maps.
55    Fts = 12,
56    /// Bitemporal history table for strict document collections.
57    ///
58    /// Keys: `{collection}:{system_from_ms_8be}:{pk_bytes}` — value is the
59    /// full Binary Tuple followed by an 8-byte big-endian `system_to_ms`
60    /// (u64::MAX = open / still-current at time of supersession).
61    StrictHistory = 13,
62    /// Bitemporal history table for graph edge collections.
63    ///
64    /// Keys: `{collection}:{edge_id_8be}:{system_from_ms_8be}` — value is
65    /// the MessagePack-encoded edge props followed by an 8-byte big-endian
66    /// `system_to_ms` (u64::MAX = current / not yet deleted).
67    GraphHistory = 14,
68    /// Bitemporal history table for schemaless document collections.
69    ///
70    /// Keys: `{collection}:{doc_id}\x00{system_from_ms:020}` — value is
71    /// `[tag:u8][valid_from_ms:i64 LE][valid_until_ms:i64 LE][body_msgpack...]`.
72    /// `tag = 0x00` (live), `0xFF` (tombstone), `0xFE` (GDPR erased).
73    DocumentHistory = 15,
74    /// O(1) pointer to the currently-live DocumentHistory version.
75    ///
76    /// Keys: `{collection}:{doc_id}` — value is the `system_from_ms` of the
77    /// live row encoded as a 20-digit zero-padded ASCII decimal (matching the
78    /// suffix used in `DocumentHistory` keys).  Absent when no live version
79    /// exists (document never written, tombstoned, or GDPR-erased).
80    ///
81    /// Written atomically alongside every `DocumentHistory` mutation so that
82    /// `versioned_get_current` can resolve the current version with one index
83    /// lookup + one history fetch instead of a full prefix scan.
84    LatestVersion = 16,
85}
86
87impl Namespace {
88    /// Convert from raw u8 (for storage layer deserialization).
89    pub fn from_u8(v: u8) -> Option<Self> {
90        match v {
91            0 => Some(Self::Meta),
92            1 => Some(Self::Vector),
93            2 => Some(Self::Graph),
94            3 => Some(Self::Crdt),
95            4 => Some(Self::LoroState),
96            5 => Some(Self::Spatial),
97            6 => Some(Self::Strict),
98            7 => Some(Self::Columnar),
99            8 => Some(Self::Kv),
100            9 => Some(Self::Array),
101            10 => Some(Self::ArrayOpLog),
102            11 => Some(Self::ArrayDelta),
103            12 => Some(Self::Fts),
104            13 => Some(Self::StrictHistory),
105            14 => Some(Self::GraphHistory),
106            15 => Some(Self::DocumentHistory),
107            16 => Some(Self::LatestVersion),
108            _ => None,
109        }
110    }
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116
117    #[test]
118    fn namespace_roundtrip() {
119        for v in 0u8..=16 {
120            let ns = Namespace::from_u8(v).unwrap();
121            assert_eq!(ns as u8, v);
122        }
123        assert!(Namespace::from_u8(17).is_none());
124    }
125}