1use std::path::{Path, PathBuf};
12use std::sync::Arc;
13
14use wasmtime::component::{Component, HasSelf, Linker, ResourceTable};
15use wasmtime::{Engine, Store};
16
17use harness::registry::VerifyFn;
18use llm_lib::attempt::VerifyReport;
19
20use crate::engine::new_engine;
21use crate::host_state::HostState;
22use crate::wasi_ctx::no_filesystem_ctx;
23
24mod bindings {
25 wasmtime::component::bindgen!({
26 path: "wit",
27 world: "verify-plugin",
28 imports: { default: async },
33 exports: { default: async },
34 });
35}
36
37impl From<bindings::exports::n7n::fix_deps::verify::VerifyReport> for VerifyReport {
41 fn from(r: bindings::exports::n7n::fix_deps::verify::VerifyReport) -> Self {
42 VerifyReport {
43 ok: r.ok,
44 output: r.output,
45 infra_error: r.infra_error,
46 }
47 }
48}
49
50impl bindings::n7n::fix_deps::host_fs::Host for HostState {
51 async fn read_file(&mut self, path: String) -> Result<String, String> {
52 std::fs::read_to_string(self.repo_root.join(path)).map_err(|e| e.to_string())
53 }
54}
55
56pub struct WasmVerify {
60 engine: Engine,
61 component: Component,
62 linker: Linker<HostState>,
63 repo_root: PathBuf,
64}
65
66impl WasmVerify {
67 pub fn from_file(
70 repo_root: impl Into<PathBuf>,
71 wasm_path: impl AsRef<Path>,
72 ) -> wasmtime::Result<Self> {
73 let engine = new_engine()?;
74 let component = Component::from_file(&engine, wasm_path.as_ref())?;
75 let mut linker = Linker::<HostState>::new(&engine);
76 wasmtime_wasi::p3::add_to_linker(&mut linker)?;
77 bindings::n7n::fix_deps::host_fs::add_to_linker::<_, HasSelf<HostState>>(
78 &mut linker,
79 |s| s,
80 )?;
81 Ok(Self {
82 engine,
83 component,
84 linker,
85 repo_root: repo_root.into(),
86 })
87 }
88
89 async fn call(&self) -> wasmtime::Result<VerifyReport> {
90 let wasi = no_filesystem_ctx();
91 let mut store = Store::new(
92 &self.engine,
93 HostState {
94 wasi,
95 table: ResourceTable::new(),
96 repo_root: self.repo_root.clone(),
97 },
98 );
99 let instance =
100 bindings::VerifyPlugin::instantiate_async(&mut store, &self.component, &self.linker)
101 .await?;
102 let report = instance.n7n_fix_deps_verify().call_run(&mut store).await?;
103 Ok(VerifyReport::from(report))
104 }
105
106 #[must_use]
115 pub fn into_verify_fn(self) -> VerifyFn {
116 let this = Arc::new(self);
117 Arc::new(move || {
118 let this = Arc::clone(&this);
119 Box::pin(async move {
120 this.call().await.unwrap_or_else(|err| VerifyReport {
121 ok: false,
122 output: format!("wasm-компонент verify: {err}"),
123 infra_error: true,
124 })
125 })
126 })
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
135
136 #[test]
137 fn bindgen_verify_report_converts_to_llm_lib_verify_report_field_by_field() {
138 let wit_report = bindings::exports::n7n::fix_deps::verify::VerifyReport {
139 ok: false,
140 output: "діагностика".to_string(),
141 infra_error: true,
142 };
143
144 let report = VerifyReport::from(wit_report);
145
146 assert!(!report.ok);
147 assert_eq!(report.output, "діагностика");
148 assert!(report.infra_error);
149 }
150}