Skip to main content

blvm_sdk/module/
storage.rs

1//! Module storage abstraction.
2//!
3//! Provides `ModuleStorage` and `ModuleTree` traits so modules can use storage
4//! without depending on a specific backend. Native modules use `DatabaseStorageAdapter`;
5//! WASM modules will use host-provided implementations.
6
7use anyhow::Result;
8use std::any::Any;
9use std::sync::Arc;
10
11/// Owned key-value pair returned by [`ModuleTree::iter`].
12pub type ModuleKvPair = (Vec<u8>, Vec<u8>);
13
14/// Key-value tree interface for module storage.
15///
16/// Minimal interface that both native (Database) and WASM (host calls) can implement.
17pub trait ModuleTree: Send + Sync {
18    /// Insert a key-value pair.
19    fn insert(&self, key: &[u8], value: &[u8]) -> Result<()>;
20
21    /// Get a value by key.
22    fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>>;
23
24    /// Remove a key.
25    fn remove(&self, key: &[u8]) -> Result<()>;
26
27    /// Iterate over all key-value pairs.
28    fn iter(&self) -> Box<dyn Iterator<Item = Result<ModuleKvPair>> + Send + '_>;
29}
30
31/// Storage interface for modules. Host implements this.
32///
33/// - **Native:** `DatabaseStorageAdapter` wraps local redb.
34/// - **WASM:** Host provides implementation via host calls.
35pub trait ModuleStorage: Send + Sync {
36    /// Open a named tree/table.
37    fn open_tree(&self, name: &str) -> Result<Arc<dyn ModuleTree>>;
38}
39
40/// Adapter: blvm-node `Tree` → `ModuleTree`.
41struct TreeAdapter(Arc<dyn blvm_node::storage::database::Tree>);
42
43impl ModuleTree for TreeAdapter {
44    fn insert(&self, key: &[u8], value: &[u8]) -> Result<()> {
45        self.0.insert(key, value)
46    }
47
48    fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
49        self.0.get(key)
50    }
51
52    fn remove(&self, key: &[u8]) -> Result<()> {
53        self.0.remove(key)
54    }
55
56    fn iter(&self) -> Box<dyn Iterator<Item = Result<ModuleKvPair>> + Send + '_> {
57        let items: Vec<_> = self.0.iter().collect();
58        Box::new(items.into_iter())
59    }
60}
61
62/// Adapter: blvm-node `Database` → `ModuleStorage`.
63///
64/// Wraps the existing database so it can be used as `ModuleStorage`.
65/// Used by native modules.
66pub struct DatabaseStorageAdapter {
67    db: Arc<dyn blvm_node::storage::database::Database>,
68}
69
70impl DatabaseStorageAdapter {
71    /// Create adapter from a database.
72    pub fn new(db: Arc<dyn blvm_node::storage::database::Database>) -> Self {
73        Self { db }
74    }
75}
76
77impl ModuleStorage for DatabaseStorageAdapter {
78    fn open_tree(&self, name: &str) -> Result<Arc<dyn ModuleTree>> {
79        let tree = self.db.open_tree(name)?;
80        let arc_tree = Arc::from(tree);
81        Ok(Arc::new(TreeAdapter(arc_tree)))
82    }
83}
84
85/// Adapter: `ModuleTree` → blvm-node `Tree`.
86///
87/// Allows `ModuleStorage` to be used where `Database` is expected (e.g. `ctx.db()`).
88struct ModuleTreeAdapter(Arc<dyn ModuleTree>);
89
90impl blvm_node::storage::database::Tree for ModuleTreeAdapter {
91    fn insert(&self, key: &[u8], value: &[u8]) -> Result<()> {
92        self.0.insert(key, value)
93    }
94
95    fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
96        self.0.get(key)
97    }
98
99    fn remove(&self, key: &[u8]) -> Result<()> {
100        self.0.remove(key)
101    }
102
103    fn contains_key(&self, key: &[u8]) -> Result<bool> {
104        Ok(self.0.get(key)?.is_some())
105    }
106
107    fn clear(&self) -> Result<()> {
108        for item in self.0.iter() {
109            let (k, _) = item?;
110            self.0.remove(&k)?;
111        }
112        Ok(())
113    }
114
115    fn len(&self) -> Result<usize> {
116        let mut n = 0;
117        for item in self.0.iter() {
118            item?;
119            n += 1;
120        }
121        Ok(n)
122    }
123
124    fn iter(&self) -> Box<dyn Iterator<Item = Result<ModuleKvPair>> + '_> {
125        self.0.iter()
126    }
127
128    fn batch(&self) -> anyhow::Result<Box<dyn blvm_node::storage::database::BatchWriter + '_>> {
129        Ok(Box::new(SimpleBatchWriter {
130            tree: self.0.clone(),
131            puts: Vec::new(),
132            deletes: Vec::new(),
133        }))
134    }
135}
136
137struct SimpleBatchWriter {
138    tree: Arc<dyn ModuleTree>,
139    puts: Vec<(Vec<u8>, Vec<u8>)>,
140    deletes: Vec<Vec<u8>>,
141}
142
143impl blvm_node::storage::database::BatchWriter for SimpleBatchWriter {
144    fn put(&mut self, key: &[u8], value: &[u8]) {
145        self.deletes.retain(|k| k.as_slice() != key);
146        self.puts.push((key.to_vec(), value.to_vec()));
147    }
148
149    fn delete(&mut self, key: &[u8]) {
150        self.puts.retain(|(k, _)| k.as_slice() != key);
151        self.deletes.push(key.to_vec());
152    }
153
154    fn commit(self: Box<Self>) -> Result<()> {
155        for key in &self.deletes {
156            self.tree.remove(key)?;
157        }
158        for (key, value) in &self.puts {
159            self.tree.insert(key, value)?;
160        }
161        Ok(())
162    }
163
164    fn len(&self) -> usize {
165        self.puts.len() + self.deletes.len()
166    }
167}
168
169/// Bridge: `ModuleStorage` → blvm-node `Database`.
170///
171/// Allows `InvocationContext` to hold `ModuleStorage` while `ctx.db()` still
172/// returns `Arc<dyn Database>` for compatibility with existing module code.
173pub struct ModuleStorageDatabaseBridge {
174    storage: Arc<dyn ModuleStorage>,
175}
176
177impl ModuleStorageDatabaseBridge {
178    /// Create bridge from module storage.
179    pub fn new(storage: Arc<dyn ModuleStorage>) -> Self {
180        Self { storage }
181    }
182}
183
184impl blvm_node::storage::database::Database for ModuleStorageDatabaseBridge {
185    fn as_any(&self) -> &dyn Any {
186        self
187    }
188
189    fn open_tree(&self, name: &str) -> Result<Box<dyn blvm_node::storage::database::Tree>> {
190        let tree = self.storage.open_tree(name)?;
191        Ok(Box::new(ModuleTreeAdapter(tree)))
192    }
193
194    fn flush(&self) -> Result<()> {
195        Ok(())
196    }
197}