use std::path::{Path, PathBuf};
use std::sync::Arc;
use wasmtime::component::{Component, HasSelf, Linker, ResourceTable};
use wasmtime::{Engine, Store};
use harness::registry::VerifyFn;
use llm_lib::attempt::VerifyReport;
use crate::engine::new_engine;
use crate::host_state::HostState;
use crate::wasi_ctx::no_filesystem_ctx;
mod bindings {
wasmtime::component::bindgen!({
path: "wit",
world: "verify-plugin",
imports: { default: async },
exports: { default: async },
});
}
impl From<bindings::exports::n7n::fix_deps::verify::VerifyReport> for VerifyReport {
fn from(r: bindings::exports::n7n::fix_deps::verify::VerifyReport) -> Self {
VerifyReport {
ok: r.ok,
output: r.output,
infra_error: r.infra_error,
}
}
}
impl bindings::n7n::fix_deps::host_fs::Host for HostState {
async fn read_file(&mut self, path: String) -> Result<String, String> {
std::fs::read_to_string(self.repo_root.join(path)).map_err(|e| e.to_string())
}
}
pub struct WasmVerify {
engine: Engine,
component: Component,
linker: Linker<HostState>,
repo_root: PathBuf,
}
impl WasmVerify {
pub fn from_file(
repo_root: impl Into<PathBuf>,
wasm_path: impl AsRef<Path>,
) -> wasmtime::Result<Self> {
let engine = new_engine()?;
let component = Component::from_file(&engine, wasm_path.as_ref())?;
let mut linker = Linker::<HostState>::new(&engine);
wasmtime_wasi::p3::add_to_linker(&mut linker)?;
bindings::n7n::fix_deps::host_fs::add_to_linker::<_, HasSelf<HostState>>(
&mut linker,
|s| s,
)?;
Ok(Self {
engine,
component,
linker,
repo_root: repo_root.into(),
})
}
async fn call(&self) -> wasmtime::Result<VerifyReport> {
let wasi = no_filesystem_ctx();
let mut store = Store::new(
&self.engine,
HostState {
wasi,
table: ResourceTable::new(),
repo_root: self.repo_root.clone(),
},
);
let instance =
bindings::VerifyPlugin::instantiate_async(&mut store, &self.component, &self.linker)
.await?;
let report = instance.n7n_fix_deps_verify().call_run(&mut store).await?;
Ok(VerifyReport::from(report))
}
#[must_use]
pub fn into_verify_fn(self) -> VerifyFn {
let this = Arc::new(self);
Arc::new(move || {
let this = Arc::clone(&this);
Box::pin(async move {
this.call().await.unwrap_or_else(|err| VerifyReport {
ok: false,
output: format!("wasm-компонент verify: {err}"),
infra_error: true,
})
})
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bindgen_verify_report_converts_to_llm_lib_verify_report_field_by_field() {
let wit_report = bindings::exports::n7n::fix_deps::verify::VerifyReport {
ok: false,
output: "діагностика".to_string(),
infra_error: true,
};
let report = VerifyReport::from(wit_report);
assert!(!report.ok);
assert_eq!(report.output, "діагностика");
assert!(report.infra_error);
}
}