Skip to main content

spacedb_store/
table.rs

1//! [`Table<K, V>`] — the typed primitive every layer above uses.
2//!
3//! A `Table` binds a table name to a key type `K` and value type `V`, and applies
4//! the two codecs ([`crate::codec`]) once so nothing above has to think about
5//! bytes: keys go through the order-preserving key encoding (so `range` returns
6//! logical order), values through the deterministic `postcard` codec.
7//!
8//! It is also the seam where the **AEAD value boundary** will live (S2): `put`
9//! will encrypt `V`'s bytes under the collection DEK before they reach the engine,
10//! and `get`/`range` will decrypt — so the engine only ever stores ciphertext.
11//! In S1 there is no crypto yet; values are stored as plaintext `postcard` bytes.
12
13use std::marker::PhantomData;
14
15use crate::codec::{decode_value, encode_value, KeyDecode, KeyEncode};
16use crate::engine::{Readable, WriteTx};
17use crate::error::StoreResult;
18
19/// A typed handle to one table. Cheap to construct and clone; holds only the
20/// table name and the `K`/`V` type binding.
21#[derive(Clone, Debug)]
22pub struct Table<K, V> {
23    name: String,
24    _types: PhantomData<fn() -> (K, V)>,
25}
26
27impl<K, V> Table<K, V>
28where
29    K: KeyEncode + KeyDecode,
30    V: serde::Serialize + serde::de::DeserializeOwned,
31{
32    /// Bind a typed table to `name`.
33    pub fn new(name: impl Into<String>) -> Self {
34        Self {
35            name: name.into(),
36            _types: PhantomData,
37        }
38    }
39
40    /// The underlying table name.
41    pub fn name(&self) -> &str {
42        &self.name
43    }
44
45    /// Fetch the value for `key`, or `None` if absent. Accepts any [`Readable`],
46    /// so it reads from a read transaction or a write transaction's own
47    /// uncommitted state.
48    pub fn get(&self, tx: &impl Readable, key: &K) -> StoreResult<Option<V>> {
49        match tx.get_raw(&self.name, &key.encode())? {
50            Some(bytes) => Ok(Some(decode_value(&bytes)?)),
51            None => Ok(None),
52        }
53    }
54
55    /// Insert or overwrite `key` → `value`.
56    pub fn put(&self, tx: &mut impl WriteTx, key: &K, value: &V) -> StoreResult<()> {
57        tx.put_raw(&self.name, &key.encode(), &encode_value(value)?)
58    }
59
60    /// Remove `key`. Returns `true` if a value was present.
61    pub fn delete(&self, tx: &mut impl WriteTx, key: &K) -> StoreResult<bool> {
62        tx.delete_raw(&self.name, &key.encode())
63    }
64
65    /// Return the decoded `(key, value)` pairs in the **half-open** range
66    /// `[lo, hi)`, in ascending logical key order.
67    pub fn range(&self, tx: &impl Readable, lo: &K, hi: &K) -> StoreResult<Vec<(K, V)>> {
68        let raw = tx.range_raw(&self.name, &lo.encode(), &hi.encode())?;
69        raw.into_iter()
70            .map(|(k, v)| Ok((K::decode(&k)?, decode_value(&v)?)))
71            .collect()
72    }
73}