use std::path::{Path, PathBuf};
use std::sync::Arc;
use wasmtime::component::{Component, HasSelf, Linker, ResourceTable};
use wasmtime::{Engine, Store};
use harness::registry::AstFactsFn;
use crate::engine::new_engine;
use crate::host_state::HostState;
use crate::wasi_ctx::read_only_repo_ctx;
mod bindings {
wasmtime::component::bindgen!({
path: "../../wit",
world: "ast-facts-plugin",
});
}
impl bindings::n7n::fix_deps::host_fs::Host for HostState {
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 WasmAstFacts {
engine: Engine,
component: Component,
linker: Linker<HostState>,
repo_root: PathBuf,
}
impl WasmAstFacts {
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::p2::add_to_linker_sync(&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(),
})
}
fn call(&self, path: &std::path::Path) -> wasmtime::Result<String> {
let wasi = read_only_repo_ctx(&self.repo_root)?;
let mut store = Store::new(
&self.engine,
HostState {
wasi,
table: ResourceTable::new(),
repo_root: self.repo_root.clone(),
},
);
let instance =
bindings::AstFactsPlugin::instantiate(&mut store, &self.component, &self.linker)?;
let facts = instance
.n7n_fix_deps_ast_facts()
.call_facts(&mut store, &path.to_string_lossy())?;
Ok(facts)
}
#[must_use]
pub fn into_ast_facts_fn(self) -> AstFactsFn {
let this = Arc::new(self);
Arc::new(move |path: PathBuf| {
let this = Arc::clone(&this);
Box::pin(async move {
this.call(&path).unwrap_or_else(|err| {
eprintln!("wasm-компонент ast-facts: {err}");
String::new()
})
})
})
}
}