auto_wasi/lib.rs
1#![warn(missing_docs)]
2//! Wrapper around [`wasmtime-wasi`](https://docs.rs/wasmtime-wasi) that automatically detects the WASI version
3//! used by the module.
4//!
5//! # Example
6//!
7//! ```rust
8//! # use auto_wasi::*;
9//! # use wasmtime::*;
10//! # use wasmtime_wasi::*;
11//! # fn test() -> anyhow::Result<()> {
12//! let wat = r#"
13//! (module
14//! (type $empty (func))
15//! ;; In the real world this would be an actual wasi import,
16//! ;; but this crate only checks the module name.
17//! (import "wasi_snapshot_preview1" "" (func (type $empty)))
18//! )
19//! "#;
20//! let store = Store::default();
21//! let ctx = WasiCtx::new(std::env::args())?;
22//!
23//! let wasm = wat::parse_str(wat)?;
24//! let wasi = AutoWasi::detect(&store, ctx, wasm)?;
25//! # Ok(()) }
26//! ```
27use anyhow::Result;
28use wasi_common::WasiCtx;
29use wasmparser::{Parser, Payload};
30use wasmtime::{Func, Linker, Store};
31
32/// An instantiated instance of the wasi exports.
33///
34/// This represents a wasi module which can be used to instantiate other wasm modules.
35/// This structure exports all that various fields of the wasi instance as fields which can be used to implement your own instantiation logic, if necessary.
36/// Additionally [`AutoWasi::get_export`](crate::AutoWasi::get_export) can be used to do name-based resolution.
37pub enum AutoWasi {
38 /// WASI imports for the old `wasi_unstable` import module.
39 Snapshot0(wasmtime_wasi::old::snapshot_0::Wasi),
40 /// WASI imports for the current `wasi_snapshot_preview1` import module.
41 Snapshot1(wasmtime_wasi::Wasi),
42}
43
44impl AutoWasi {
45 /// Creates a new [`AutoWasi`](crate::AutoWasi) that allows for linking from the detected
46 /// wasi version.
47 pub fn detect<T: AsRef<[u8]>>(store: &Store, ctx: WasiCtx, binary: T) -> Result<Self> {
48 let version = WasiVersion::detect(binary)?;
49 Ok(Self::new(store, ctx, version))
50 }
51
52 /// Creates a new [`AutoWasi`](crate::AutoWasi) that allows for linking from the provided
53 /// [`WasiVersion`](crate::WasiVersion).
54 pub fn new(store: &Store, ctx: WasiCtx, version: WasiVersion) -> Self {
55 match version {
56 WasiVersion::Snapshot0 => {
57 let wasi = wasmtime_wasi::old::snapshot_0::Wasi::new(&store, ctx);
58 Self::Snapshot0(wasi)
59 }
60 WasiVersion::Snapshot1 => {
61 let wasi = wasmtime_wasi::Wasi::new(&store, ctx);
62 Self::Snapshot1(wasi)
63 }
64 }
65 }
66
67 /// Looks up a field called name in this structure, returning it if found.
68 /// This is often useful when instantiating a wasmtime instance where name resolution often happens with strings.
69 pub fn get_export(&self, name: &str) -> Option<&Func> {
70 match self {
71 Self::Snapshot0(wasi) => wasi.get_export(name),
72 Self::Snapshot1(wasi) => wasi.get_export(name),
73 }
74 }
75
76 /// Adds all instance items to the specified Linker.
77 pub fn add_to_linker(&self, linker: &mut Linker) -> Result<()> {
78 match self {
79 Self::Snapshot0(wasi) => wasi.add_to_linker(linker),
80 Self::Snapshot1(wasi) => wasi.add_to_linker(linker),
81 }
82 }
83}
84
85/// The version of WASI that a binary relies on.
86#[derive(Debug, Clone, PartialEq, Eq)]
87pub enum WasiVersion {
88 /// Called `wasi_unstable` in binaries.
89 Snapshot0,
90 /// Called `wasi_snapshot_preview1` in binaries.
91 Snapshot1,
92}
93
94impl WasiVersion {
95 /// Detects the WASI version used by the binary, defaults to the latest.
96 pub fn detect<T: AsRef<[u8]>>(binary: T) -> Result<Self> {
97 for payload in Parser::new(0).parse_all(binary.as_ref()) {
98 match payload? {
99 Payload::ImportSection(reader) => {
100 for import in reader {
101 if import?.module == "wasi_unstable" {
102 return Ok(Self::Snapshot0);
103 }
104 }
105 }
106 _ => {}
107 }
108 }
109
110 Ok(Self::default())
111 }
112}
113
114impl Default for WasiVersion {
115 fn default() -> Self {
116 Self::Snapshot1
117 }
118}