use wasmtime::component::Linker;
use crate::bindings::{VfsFilesystem, preopens, types};
use crate::hybrid::HybridVfsState;
use crate::hybrid_bindings::{
HybridFilesystem, preopens as hybrid_preopens, types as hybrid_types,
};
use crate::storage::VfsStorage;
use crate::wasi_impl::VfsState;
pub trait VfsView: Send {
type Storage: VfsStorage + Clone + 'static;
fn vfs(&mut self) -> VfsState<'_, Self::Storage>;
}
pub fn add_vfs_to_linker<T>(linker: &mut Linker<T>) -> anyhow::Result<()>
where
T: VfsView + 'static,
{
types::add_to_linker::<T, VfsFilesystem<T::Storage>>(linker, |state| state.vfs())?;
preopens::add_to_linker::<T, VfsFilesystem<T::Storage>>(linker, |state| state.vfs())?;
Ok(())
}
pub trait HybridVfsView: Send {
type Storage: VfsStorage + Clone + 'static;
fn hybrid_vfs(&mut self) -> HybridVfsState<'_, Self::Storage>;
}
pub fn add_hybrid_vfs_to_linker<T>(linker: &mut Linker<T>) -> anyhow::Result<()>
where
T: HybridVfsView + 'static,
{
hybrid_types::add_to_linker::<T, HybridFilesystem<T::Storage>>(linker, |state| {
state.hybrid_vfs()
})?;
hybrid_preopens::add_to_linker::<T, HybridFilesystem<T::Storage>>(linker, |state| {
state.hybrid_vfs()
})?;
Ok(())
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use wasmtime::Engine;
use wasmtime::component::{Linker, ResourceTable};
use wasmtime_wasi::{WasiCtx, WasiCtxView, WasiView};
use super::{HybridVfsView, VfsView, add_hybrid_vfs_to_linker, add_vfs_to_linker};
use crate::hybrid::{HybridVfsCtx, HybridVfsState};
use crate::storage::ArcStorage;
use crate::wasi_impl::{VfsCtx, VfsState};
struct TestState {
wasi: WasiCtx,
table: ResourceTable,
vfs: VfsCtx<ArcStorage>,
hybrid: HybridVfsCtx<ArcStorage>,
}
impl WasiView for TestState {
fn ctx(&mut self) -> WasiCtxView<'_> {
WasiCtxView {
ctx: &mut self.wasi,
table: &mut self.table,
}
}
}
impl VfsView for TestState {
type Storage = ArcStorage;
fn vfs(&mut self) -> VfsState<'_, Self::Storage> {
VfsState {
ctx: &mut self.vfs,
table: &mut self.table,
}
}
}
impl HybridVfsView for TestState {
type Storage = ArcStorage;
fn hybrid_vfs(&mut self) -> HybridVfsState<'_, Self::Storage> {
HybridVfsState::new(&mut self.hybrid, &mut self.table)
}
}
fn wasi_linker() -> Linker<TestState> {
let engine = Engine::default();
let mut linker = Linker::<TestState>::new(&engine);
wasmtime_wasi::p2::add_to_linker_async(&mut linker).unwrap();
linker
}
#[test]
fn linker_shadows_wasmtime_wasi_filesystem() {
const COLLIDING_ENTRY: &str = "descriptor";
for (label, result) in [
("add_vfs_to_linker", add_vfs_to_linker(&mut wasi_linker())),
(
"add_hybrid_vfs_to_linker",
add_hybrid_vfs_to_linker(&mut wasi_linker()),
),
] {
let err = result.err().unwrap_or_else(|| {
panic!(
"{label} did not collide with wasmtime-wasi's filesystem: the \
wasi:filesystem version in crates/eryx-vfs/wit must be bumped to \
match the one wasmtime-wasi ships"
)
});
let err = format!("{err:#}");
assert!(
err.contains("defined twice") && err.contains(COLLIDING_ENTRY),
"{label} failed for an unexpected reason: {err}"
);
}
}
}