1use std::sync::Arc;
7
8use anyhow::{Result, bail};
9use async_trait::async_trait;
10use surrealism_types::err::{PrefixErr, SurrealismResult};
11use wasmtime::StoreContextMut;
12
13use crate::config::SurrealismConfig;
14use crate::kv::KVStore;
15use crate::store::StoreData;
16
17#[async_trait]
24pub trait InvocationContext: Send + Sync {
25 async fn sql(
26 &mut self,
27 config: &SurrealismConfig,
28 query: String,
29 vars: surrealdb_types::Object,
30 ) -> Result<surrealdb_types::Value>;
31 async fn run(
32 &mut self,
33 config: &SurrealismConfig,
34 fnc: String,
35 version: Option<String>,
36 args: Vec<surrealdb_types::Value>,
37 ) -> Result<surrealdb_types::Value>;
38
39 fn kv(&mut self) -> Result<&dyn KVStore>;
40
41 fn stdout(&mut self, output: &str) -> Result<()> {
42 print!("{}", output);
43 Ok(())
44 }
45
46 fn stderr(&mut self, output: &str) -> Result<()> {
47 eprint!("{}", output);
48 Ok(())
49 }
50
51 fn stdout_callback(&self) -> Arc<dyn Fn(&str) + Send + Sync> {
61 Arc::new(|output| print!("{}", output))
62 }
63
64 fn stderr_callback(&self) -> Arc<dyn Fn(&str) + Send + Sync> {
66 Arc::new(|output| eprint!("{}", output))
67 }
68}
69
70pub(crate) struct NullContext;
75
76#[async_trait]
77impl InvocationContext for NullContext {
78 async fn sql(
79 &mut self,
80 _config: &SurrealismConfig,
81 _query: String,
82 _vars: surrealdb_types::Object,
83 ) -> Result<surrealdb_types::Value> {
84 bail!("no active invocation context")
85 }
86
87 async fn run(
88 &mut self,
89 _config: &SurrealismConfig,
90 _fnc: String,
91 _version: Option<String>,
92 _args: Vec<surrealdb_types::Value>,
93 ) -> Result<surrealdb_types::Value> {
94 bail!("no active invocation context")
95 }
96
97 fn kv(&mut self) -> Result<&dyn KVStore> {
98 bail!("no active invocation context")
99 }
100}
101
102fn decode_range_bounds(
107 bytes: &[u8],
108) -> Result<(std::ops::Bound<String>, std::ops::Bound<String>), String> {
109 surrealdb_types::decode_string_range(bytes).map_err(|e| e.to_string())
110}
111
112fn stringify<E: std::fmt::Display>(e: E) -> String {
113 e.to_string()
114}
115
116macro_rules! register_host_fn {
126 ($host:ident, $name:literal,
127 |$store:ident, ($($arg:ident : $ty:ty),* $(,)?)| -> Result<$ret:ty> $body:block
128 ) => {
129 $host
130 .func_wrap_async(
131 $name,
132 |mut $store: StoreContextMut<'_, StoreData>,
133 ($($arg,)*): ($($ty,)*)| {
134 Box::new(async move {
135 let inner: Result<$ret, String> = async $body.await;
136 Ok((inner,))
137 })
138 },
139 )
140 .prefix_err(|| concat!("failed to register ", $name))?;
141 };
142}
143
144pub fn implement_host_functions(
145 linker: &mut wasmtime::component::Linker<StoreData>,
146) -> SurrealismResult<()> {
147 let mut root = linker.root();
148 let mut host =
149 root.instance("surrealism:plugin/host").prefix_err(|| "failed to define host instance")?;
150
151 register_host_fn!(host, "sql",
152 |store, (query: String, vars_bytes: Vec<u8>)| -> Result<Vec<u8>> {
153 let vars_vec = surrealdb_types::decode_string_key_values(&vars_bytes)
154 .map_err(stringify)?;
155 let vars = surrealdb_types::Object::from_iter(vars_vec.into_iter());
156 let config = Arc::clone(&store.data().config);
157 let val = store.data_mut().context.sql(&config, query, vars).await.map_err(stringify)?;
158 surrealdb_types::encode(&val).map_err(stringify)
159 }
160 );
161
162 register_host_fn!(host, "run",
163 |store, (fnc: String, version: Option<String>, args_bytes: Vec<u8>)| -> Result<Vec<u8>> {
164 let args = surrealdb_types::decode_value_list(&args_bytes).map_err(stringify)?;
165 let config = Arc::clone(&store.data().config);
166 let val = store.data_mut().context.run(&config, fnc, version, args).await.map_err(stringify)?;
167 surrealdb_types::encode(&val).map_err(stringify)
168 }
169 );
170
171 register_host_fn!(host, "kv-get",
172 |store, (key: String)| -> Result<Option<Vec<u8>>> {
173 let kv = store.data_mut().context.kv().map_err(stringify)?;
174 match kv.get(key).await.map_err(stringify)? {
175 Some(v) => surrealdb_types::encode(&v).map(Some).map_err(stringify),
176 None => Ok(None),
177 }
178 }
179 );
180
181 register_host_fn!(host, "kv-set",
182 |store, (key: String, value_bytes: Vec<u8>)| -> Result<()> {
183 let value: surrealdb_types::Value =
184 surrealdb_types::decode(&value_bytes).map_err(stringify)?;
185 let kv = store.data_mut().context.kv().map_err(stringify)?;
186 kv.set(key, value).await.map_err(stringify)
187 }
188 );
189
190 register_host_fn!(host, "kv-del",
191 |store, (key: String)| -> Result<()> {
192 let kv = store.data_mut().context.kv().map_err(stringify)?;
193 kv.del(key).await.map_err(stringify)
194 }
195 );
196
197 register_host_fn!(host, "kv-exists",
198 |store, (key: String)| -> Result<bool> {
199 let kv = store.data_mut().context.kv().map_err(stringify)?;
200 kv.exists(key).await.map_err(stringify)
201 }
202 );
203
204 register_host_fn!(host, "kv-del-rng",
205 |store, (range_bytes: Vec<u8>)| -> Result<()> {
206 let (start, end) = decode_range_bounds(&range_bytes)?;
207 let kv = store.data_mut().context.kv().map_err(stringify)?;
208 kv.del_rng(start, end).await.map_err(stringify)
209 }
210 );
211
212 register_host_fn!(host, "kv-get-batch",
213 |store, (keys: Vec<String>)| -> Result<Vec<u8>> {
214 let kv = store.data_mut().context.kv().map_err(stringify)?;
215 let vals = kv.get_batch(keys).await.map_err(stringify)?;
216 surrealdb_types::encode_optional_values(&vals).map_err(stringify)
217 }
218 );
219
220 register_host_fn!(host, "kv-set-batch",
221 |store, (entries_bytes: Vec<u8>)| -> Result<()> {
222 let entries = surrealdb_types::decode_string_key_values(&entries_bytes)
223 .map_err(stringify)?;
224 let kv = store.data_mut().context.kv().map_err(stringify)?;
225 kv.set_batch(entries).await.map_err(stringify)
226 }
227 );
228
229 register_host_fn!(host, "kv-del-batch",
230 |store, (keys: Vec<String>)| -> Result<()> {
231 let kv = store.data_mut().context.kv().map_err(stringify)?;
232 kv.del_batch(keys).await.map_err(stringify)
233 }
234 );
235
236 register_host_fn!(host, "kv-keys",
237 |store, (range_bytes: Vec<u8>)| -> Result<Vec<String>> {
238 let (start, end) = decode_range_bounds(&range_bytes)?;
239 let kv = store.data_mut().context.kv().map_err(stringify)?;
240 kv.keys(start, end).await.map_err(stringify)
241 }
242 );
243
244 register_host_fn!(host, "kv-values",
245 |store, (range_bytes: Vec<u8>)| -> Result<Vec<u8>> {
246 let (start, end) = decode_range_bounds(&range_bytes)?;
247 let kv = store.data_mut().context.kv().map_err(stringify)?;
248 let vals = kv.values(start, end).await.map_err(stringify)?;
249 surrealdb_types::encode_value_list(&vals).map_err(stringify)
250 }
251 );
252
253 register_host_fn!(host, "kv-entries",
254 |store, (range_bytes: Vec<u8>)| -> Result<Vec<u8>> {
255 let (start, end) = decode_range_bounds(&range_bytes)?;
256 let kv = store.data_mut().context.kv().map_err(stringify)?;
257 let entries = kv.entries(start, end).await.map_err(stringify)?;
258 surrealdb_types::encode_string_key_values(&entries).map_err(stringify)
259 }
260 );
261
262 register_host_fn!(host, "kv-count",
263 |store, (range_bytes: Vec<u8>)| -> Result<u64> {
264 let (start, end) = decode_range_bounds(&range_bytes)?;
265 let kv = store.data_mut().context.kv().map_err(stringify)?;
266 kv.count(start, end).await.map_err(stringify)
267 }
268 );
269
270 Ok(())
271}