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::no_filesystem_ctx;
15
16mod bindings {
17 wasmtime::component::bindgen!({
18 path: "wit",
19 world: "ast-facts-plugin",
20 imports: { default: async },
25 exports: { default: async },
26 });
27}
28
29impl bindings::n7n::fix_deps::host_fs::Host for HostState {
30 async fn read_file(&mut self, path: String) -> Result<String, String> {
31 std::fs::read_to_string(self.repo_root.join(path)).map_err(|e| e.to_string())
32 }
33}
34
35pub struct WasmAstFacts {
38 engine: Engine,
39 component: Component,
40 linker: Linker<HostState>,
41 repo_root: PathBuf,
42}
43
44impl WasmAstFacts {
45 pub fn from_file(
47 repo_root: impl Into<PathBuf>,
48 wasm_path: impl AsRef<Path>,
49 ) -> wasmtime::Result<Self> {
50 let engine = new_engine()?;
51 let component = Component::from_file(&engine, wasm_path.as_ref())?;
52 let mut linker = Linker::<HostState>::new(&engine);
53 wasmtime_wasi::p3::add_to_linker(&mut linker)?;
54 bindings::n7n::fix_deps::host_fs::add_to_linker::<_, HasSelf<HostState>>(
55 &mut linker,
56 |s| s,
57 )?;
58 Ok(Self {
59 engine,
60 component,
61 linker,
62 repo_root: repo_root.into(),
63 })
64 }
65
66 async fn call(&self, path: &std::path::Path) -> wasmtime::Result<String> {
67 let wasi = no_filesystem_ctx();
68 let mut store = Store::new(
69 &self.engine,
70 HostState {
71 wasi,
72 table: ResourceTable::new(),
73 repo_root: self.repo_root.clone(),
74 },
75 );
76 let instance =
77 bindings::AstFactsPlugin::instantiate_async(&mut store, &self.component, &self.linker)
78 .await?;
79 let facts = instance
80 .n7n_fix_deps_ast_facts()
81 .call_facts(&mut store, &path.to_string_lossy())
82 .await?;
83 Ok(facts)
84 }
85
86 #[must_use]
95 pub fn into_ast_facts_fn(self) -> AstFactsFn {
96 let this = Arc::new(self);
97 Arc::new(move |path: PathBuf| {
98 let this = Arc::clone(&this);
99 Box::pin(async move {
100 this.call(&path).await.unwrap_or_else(|err| {
101 eprintln!("wasm-компонент ast-facts: {err}");
102 String::new()
103 })
104 })
105 })
106 }
107}