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}