use std::ops::{Deref, DerefMut};
use anyhow::Result;
use async_trait::async_trait;
use surrealism_types::controller::AsyncMemoryController;
use surrealism_types::err::PrefixError;
use surrealism_types::serialize::SerializableRange;
use surrealism_types::transfer::AsyncTransfer;
use wasmtime::{Caller, Linker};
use crate::config::SurrealismConfig;
use crate::controller::StoreData;
use crate::kv::KVStore;
macro_rules! host_try_or_return {
($error:expr,$expr:expr) => {
match $expr {
Ok(val) => val,
Err(e) => {
eprintln!("{}: {}", $error, e);
return -1;
}
}
};
}
macro_rules! force_u32 {
($ty:ty) => {
u32
};
}
#[macro_export]
macro_rules! register_host_function {
($linker:expr, $name:expr, |mut $controller:ident : $controller_ty:ty, $arg:ident : $arg_ty:ty| -> Result<$ret:ty> $body:tt) => {{
$linker
.func_wrap_async(
"env",
$name,
|caller: Caller<'_, StoreData>, ($arg,): (u32,)| {
Box::new(async move {
eprintln!("🔵 Host function called: {}", $name);
let mut $controller: $controller_ty = HostController::from(caller);
let $arg = host_try_or_return!("Failed to receive argument", <$arg_ty>::receive($arg.into(), &mut $controller).await);
eprintln!("🟡 Executing async body for: {}", $name);
let result = $body;
eprintln!("🟢 Async body completed for: {}", $name);
(*host_try_or_return!("Transfer error", result.transfer(&mut $controller).await)) as i32
})
}
)
.prefix_err(|| "failed to register host function")?
}};
($linker:expr, $name:expr, |mut $controller:ident : $controller_ty:ty, $($arg:ident : $arg_ty:ty),+| -> Result<$ret:ty> $body:tt) => {{
$linker
.func_wrap_async(
"env",
$name,
|caller: Caller<'_, StoreData>, ($($arg),+): ($(force_u32!($arg_ty)),+)| {
Box::new(async move {
eprintln!("🔵 Host function called: {}", $name);
let mut $controller: $controller_ty = HostController::from(caller);
$(let $arg = host_try_or_return!("Failed to receive argument", <$arg_ty>::receive($arg.into(), &mut $controller).await);)+
eprintln!("🟡 Executing async body for: {}", $name);
let result = $body;
eprintln!("🟢 Async body completed for: {}", $name);
(*host_try_or_return!("Transfer error", result.transfer(&mut $controller).await)) as i32
})
}
)
.prefix_err(|| "failed to register host function")?
}};
($linker:expr, $name:expr, |$controller:ident : $controller_ty:ty, $($arg:ident : $arg_ty:ty),+| -> Result<$ret:ty> $body:tt) => {{
$linker
.func_wrap_async(
"env",
$name,
|caller: Caller<'_, StoreData>, ($($arg),+): ($(force_u32!($arg_ty)),+)| {
Box::new(async move {
eprintln!("🔵 Host function called: {}", $name);
let mut $controller: $controller_ty = HostController::from(caller);
$(let $arg = host_try_or_return!("Failed to receive argument", <$arg_ty>::receive($arg.into(), &mut $controller).await);)+
eprintln!("🟡 Executing async body for: {}", $name);
let result = $body;
eprintln!("🟢 Async body completed for: {}", $name);
(*host_try_or_return!("Transfer error", result.transfer(&mut $controller).await)) as i32
})
}
)
.prefix_err(|| "failed to register host function")?
}};
}
macro_rules! map_ok {
($expr:expr => |$x:ident| $body:expr) => {
match $expr {
Ok($x) => $body,
Err(e) => Err(e),
}
};
}
#[async_trait]
pub trait InvocationContext: Send + Sync {
async fn sql(
&mut self,
config: &SurrealismConfig,
query: String,
vars: surrealdb_types::Object,
) -> Result<surrealdb_types::Value>;
async fn run(
&mut self,
config: &SurrealismConfig,
fnc: String,
version: Option<String>,
args: Vec<surrealdb_types::Value>,
) -> Result<surrealdb_types::Value>;
fn kv(&mut self) -> Result<&dyn KVStore>;
fn stdout(&mut self, output: &str) -> Result<()> {
print!("{}", output);
Ok(())
}
fn stderr(&mut self, output: &str) -> Result<()> {
eprint!("{}", output);
Ok(())
}
}
pub trait Host: InvocationContext {}
pub fn implement_host_functions(linker: &mut Linker<StoreData>) -> Result<()> {
#[rustfmt::skip]
register_host_function!(linker, "__sr_sql", |mut controller: HostController, sql: String, vars: Vec<(String, surrealdb_types::Value)>| -> Result<surrealdb_types::Value> {
let vars = surrealdb_types::Object::from_iter(vars.into_iter());
let config = controller.config().clone();
controller.context_mut().sql(&config, sql, vars).await
});
#[rustfmt::skip]
register_host_function!(linker, "__sr_run", |mut controller: HostController, fnc: String, version: Option<String>, args: Vec<surrealdb_types::Value>| -> Result<surrealdb_types::Value> {
let config = controller.config().clone();
controller.context_mut().run(&config, fnc, version, args).await
});
#[rustfmt::skip]
register_host_function!(linker, "__sr_kv_get", |mut controller: HostController, key: String| -> Result<Option<surrealdb_types::Value>> {
map_ok!(controller.context_mut().kv() => |kv| kv.get(key).await)
});
#[rustfmt::skip]
register_host_function!(linker, "__sr_kv_set", |mut controller: HostController, key: String, value: surrealdb_types::Value| -> Result<()> {
map_ok!(controller.context_mut().kv() => |kv| kv.set(key, value).await)
});
#[rustfmt::skip]
register_host_function!(linker, "__sr_kv_del", |mut controller: HostController, key: String| -> Result<()> {
map_ok!(controller.context_mut().kv() => |kv| kv.del(key).await)
});
#[rustfmt::skip]
register_host_function!(linker, "__sr_kv_exists", |mut controller: HostController, key: String| -> Result<bool> {
map_ok!(controller.context_mut().kv() => |kv| kv.exists(key).await)
});
#[rustfmt::skip]
register_host_function!(linker, "__sr_kv_del_rng", |mut controller: HostController, range: SerializableRange<String>| -> Result<()> {
map_ok!(controller.context_mut().kv() => |kv| kv.del_rng(range.beg, range.end).await)
});
#[rustfmt::skip]
register_host_function!(linker, "__sr_kv_get_batch", |mut controller: HostController, keys: Vec<String>| -> Result<Vec<Option<surrealdb_types::Value>>> {
map_ok!(controller.context_mut().kv() => |kv| kv.get_batch(keys).await)
});
#[rustfmt::skip]
register_host_function!(linker, "__sr_kv_set_batch", |mut controller: HostController, entries: Vec<(String, surrealdb_types::Value)>| -> Result<()> {
map_ok!(controller.context_mut().kv() => |kv| kv.set_batch(entries).await)
});
#[rustfmt::skip]
register_host_function!(linker, "__sr_kv_del_batch", |mut controller: HostController, keys: Vec<String>| -> Result<()> {
map_ok!(controller.context_mut().kv() => |kv| kv.del_batch(keys).await)
});
#[rustfmt::skip]
register_host_function!(linker, "__sr_kv_keys", |mut controller: HostController, range: SerializableRange<String>| -> Result<Vec<String>> {
map_ok!(controller.context_mut().kv() => |kv| kv.keys(range.beg, range.end).await)
});
#[rustfmt::skip]
register_host_function!(linker, "__sr_kv_values", |mut controller: HostController, range: SerializableRange<String>| -> Result<Vec<surrealdb_types::Value>> {
map_ok!(controller.context_mut().kv() => |kv| kv.values(range.beg, range.end).await)
});
#[rustfmt::skip]
register_host_function!(linker, "__sr_kv_entries", |mut controller: HostController, range: SerializableRange<String>| -> Result<Vec<(String, surrealdb_types::Value)>> {
map_ok!(controller.context_mut().kv() => |kv| kv.entries(range.beg, range.end).await)
});
#[rustfmt::skip]
register_host_function!(linker, "__sr_kv_count", |mut controller: HostController, range: SerializableRange<String>| -> Result<u64> {
map_ok!(controller.context_mut().kv() => |kv| kv.count(range.beg, range.end).await)
});
Ok(())
}
struct HostController<'a>(Caller<'a, StoreData>);
impl<'a> HostController<'a> {
pub fn context_mut(&mut self) -> &mut dyn InvocationContext {
&mut *self.0.data_mut().context
}
pub fn config(&self) -> &SurrealismConfig {
&self.0.data().config
}
}
impl<'a> From<Caller<'a, StoreData>> for HostController<'a> {
fn from(caller: Caller<'a, StoreData>) -> Self {
Self(caller)
}
}
impl<'a> Deref for HostController<'a> {
type Target = Caller<'a, StoreData>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<'a> DerefMut for HostController<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[async_trait]
impl<'a> AsyncMemoryController for HostController<'a> {
async fn alloc(&mut self, len: u32) -> Result<u32> {
let alloc_func = self
.get_export("__sr_alloc")
.ok_or_else(|| anyhow::anyhow!("Export __sr_alloc not found"))?
.into_func()
.ok_or_else(|| anyhow::anyhow!("Export __sr_alloc is not a function"))?;
let result =
alloc_func.typed::<(u32,), u32>(&mut self.0)?.call_async(&mut self.0, (len,)).await?;
if result == 0 {
anyhow::bail!("Memory allocation failed");
}
Ok(result)
}
async fn free(&mut self, ptr: u32, len: u32) -> Result<()> {
let free_func = self
.get_export("__sr_free")
.ok_or_else(|| anyhow::anyhow!("Export __sr_free not found"))?
.into_func()
.ok_or_else(|| anyhow::anyhow!("Export __sr_free is not a function"))?;
let result = free_func
.typed::<(u32, u32), u32>(&mut self.0)?
.call_async(&mut self.0, (ptr, len))
.await?;
if result == 0 {
anyhow::bail!("Memory deallocation failed");
}
Ok(())
}
fn mut_mem(&mut self, ptr: u32, len: u32) -> Result<&mut [u8]> {
let memory = self
.get_export("memory")
.ok_or_else(|| anyhow::anyhow!("Export memory not found"))?
.into_memory()
.ok_or_else(|| anyhow::anyhow!("Export memory is not a memory"))?;
let mem = memory.data_mut(&mut self.0);
if (ptr as usize) + (len as usize) > mem.len() {
anyhow::bail!(
"[ERROR] Out of bounds: ptr + len = {} > mem.len() = {}",
(ptr as usize) + (len as usize),
mem.len()
);
}
Ok(&mut mem[(ptr as usize)..(ptr as usize) + (len as usize)])
}
}