Skip to main content

crabllm_core/
storage.rs

1use crate::Error;
2use std::{future::Future, pin::Pin};
3
4/// Fixed-length prefix size in bytes for storage keys.
5pub const PREFIX_LEN: usize = 4;
6
7/// A fixed-length prefix that namespaces storage keys per extension.
8///
9/// The first `PREFIX_LEN` bytes of every key identify which extension
10/// owns the data. The remaining bytes are the extension-specific key.
11pub type Prefix = [u8; PREFIX_LEN];
12
13/// Key-value pairs returned by `Storage::list`.
14pub type KvPairs = Vec<(Vec<u8>, Vec<u8>)>;
15
16/// A pinned, boxed, Send future. Used for dyn-compatible async trait methods.
17pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
18
19/// Build a full storage key from a fixed-length prefix and a suffix.
20pub fn storage_key(prefix: &Prefix, suffix: &[u8]) -> Vec<u8> {
21    let mut key = Vec::with_capacity(PREFIX_LEN + suffix.len());
22    key.extend_from_slice(prefix);
23    key.extend_from_slice(suffix);
24    key
25}
26
27/// Generic async key-value storage backend for extensions.
28///
29/// Keys are raw bytes. The first `PREFIX_LEN` bytes are a fixed-width
30/// namespace prefix. All methods take `&self` and are dyn-compatible
31/// via `BoxFuture`. Implementations must be `Send + Sync`.
32pub trait Storage: Send + Sync {
33    /// Get a value by key. Returns `None` if the key does not exist.
34    fn get(&self, key: &[u8]) -> BoxFuture<'_, Result<Option<Vec<u8>>, Error>>;
35
36    /// Set a key to a value, overwriting any existing value.
37    fn set(&self, key: &[u8], value: Vec<u8>) -> BoxFuture<'_, Result<(), Error>>;
38
39    /// Atomically increment a counter by `delta`, returning the new value.
40    /// If the key does not exist, it is created with an initial value of `delta`.
41    fn increment(&self, key: &[u8], delta: i64) -> BoxFuture<'_, Result<i64, Error>>;
42
43    /// List all key-value pairs whose keys start with `prefix`.
44    /// Returned in arbitrary order.
45    fn list(&self, prefix: &Prefix) -> BoxFuture<'_, Result<KvPairs, Error>>;
46
47    /// Delete a key. No-op if the key does not exist.
48    fn delete(&self, key: &[u8]) -> BoxFuture<'_, Result<(), Error>>;
49}
50
51/// Empty storage backend. Every method returns an error. Use as the `S`
52/// parameter in `AppState<S, P>` when embedding the gateway with no
53/// extensions configured — anything that does call into storage is a
54/// misconfiguration that should fail loudly rather than silently drop
55/// data or report fake "not found" reads.
56impl Storage for () {
57    fn get(&self, _key: &[u8]) -> BoxFuture<'_, Result<Option<Vec<u8>>, Error>> {
58        Box::pin(async { Err(Error::Internal("storage not configured".into())) })
59    }
60
61    fn set(&self, _key: &[u8], _value: Vec<u8>) -> BoxFuture<'_, Result<(), Error>> {
62        Box::pin(async { Err(Error::Internal("storage not configured".into())) })
63    }
64
65    fn increment(&self, _key: &[u8], _delta: i64) -> BoxFuture<'_, Result<i64, Error>> {
66        Box::pin(async { Err(Error::Internal("storage not configured".into())) })
67    }
68
69    fn list(&self, _prefix: &Prefix) -> BoxFuture<'_, Result<KvPairs, Error>> {
70        Box::pin(async { Err(Error::Internal("storage not configured".into())) })
71    }
72
73    fn delete(&self, _key: &[u8]) -> BoxFuture<'_, Result<(), Error>> {
74        Box::pin(async { Err(Error::Internal("storage not configured".into())) })
75    }
76}