1use std::path::{Path, PathBuf};
5use std::sync::Arc;
6
7use wasmtime::component::{Component, HasSelf, Linker, ResourceTable};
8use wasmtime::{Engine, Store};
9
10use harness::registry::AstFactsFn;
11
12use crate::engine::new_engine;
13use crate::host_state::HostState;
14use crate::wasi_ctx::read_only_repo_ctx;
15
16mod bindings {
17 wasmtime::component::bindgen!({
18 path: "wit",
19 world: "ast-facts-plugin",
20 });
21}
22
23impl bindings::n7n::fix_deps::host_fs::Host for HostState {
24 fn read_file(&mut self, path: String) -> Result<String, String> {
25 std::fs::read_to_string(self.repo_root.join(path)).map_err(|e| e.to_string())
26 }
27}
28
29pub struct WasmAstFacts {
32 engine: Engine,
33 component: Component,
34 linker: Linker<HostState>,
35 repo_root: PathBuf,
36}
37
38impl WasmAstFacts {
39 pub fn from_file(
41 repo_root: impl Into<PathBuf>,
42 wasm_path: impl AsRef<Path>,
43 ) -> wasmtime::Result<Self> {
44 let engine = new_engine()?;
45 let component = Component::from_file(&engine, wasm_path.as_ref())?;
46 let mut linker = Linker::<HostState>::new(&engine);
47 wasmtime_wasi::p2::add_to_linker_sync(&mut linker)?;
48 bindings::n7n::fix_deps::host_fs::add_to_linker::<_, HasSelf<HostState>>(
49 &mut linker,
50 |s| s,
51 )?;
52 Ok(Self {
53 engine,
54 component,
55 linker,
56 repo_root: repo_root.into(),
57 })
58 }
59
60 fn call(&self, path: &std::path::Path) -> wasmtime::Result<String> {
61 let wasi = read_only_repo_ctx(&self.repo_root)?;
62 let mut store = Store::new(
63 &self.engine,
64 HostState {
65 wasi,
66 table: ResourceTable::new(),
67 repo_root: self.repo_root.clone(),
68 },
69 );
70 let instance =
71 bindings::AstFactsPlugin::instantiate(&mut store, &self.component, &self.linker)?;
72 let facts = instance
73 .n7n_fix_deps_ast_facts()
74 .call_facts(&mut store, &path.to_string_lossy())?;
75 Ok(facts)
76 }
77
78 #[must_use]
87 pub fn into_ast_facts_fn(self) -> AstFactsFn {
88 let this = Arc::new(self);
89 Arc::new(move |path: PathBuf| {
90 let this = Arc::clone(&this);
91 Box::pin(async move {
92 this.call(&path).unwrap_or_else(|err| {
93 eprintln!("wasm-компонент ast-facts: {err}");
94 String::new()
95 })
96 })
97 })
98 }
99}