Skip to main content

wasmtime_wasi/p3/cli/
mod.rs

1mod host;
2
3use crate::cli::{WasiCli, WasiCliNamed, WasiCliNamedView, WasiCliView};
4use crate::p3::Interface;
5use crate::p3::bindings::cli::{
6    environment, exit, stderr, stdin, stdout, terminal_input, terminal_output, terminal_stderr,
7    terminal_stdin, terminal_stdout,
8};
9use crate::{NamedId, WasiCtxNamedView};
10use wasmtime::component::{Component, Linker};
11
12/// Add all WASI interfaces from this module into the `linker` provided.
13///
14/// This function will add all interfaces implemented by this module to the
15/// [`Linker`], which corresponds to the `wasi:cli/imports` world supported by
16/// this module.
17///
18/// This is low-level API for advanced use cases,
19/// [`wasmtime_wasi::p3::add_to_linker`](crate::p3::add_to_linker) can be used instead
20/// to add *all* wasip3 interfaces (including the ones from this module) to the `linker`.
21///
22/// # Example
23///
24/// ```
25/// use wasmtime::{Engine, Result, Store, Config};
26/// use wasmtime::component::{Linker, ResourceTable};
27/// use wasmtime_wasi::cli::{WasiCliCtx, WasiCliView, WasiCliCtxView};
28///
29/// fn main() -> Result<()> {
30///     let mut config = Config::new();
31///     config.wasm_component_model_async(true);
32///     let engine = Engine::new(&config)?;
33///
34///     let mut linker = Linker::<MyState>::new(&engine);
35///     wasmtime_wasi::p3::cli::add_to_linker(&mut linker)?;
36///     // ... add any further functionality to `linker` if desired ...
37///
38///     let mut store = Store::new(
39///         &engine,
40///         MyState::default(),
41///     );
42///
43///     // ... use `linker` to instantiate within `store` ...
44///
45///     Ok(())
46/// }
47///
48/// #[derive(Default)]
49/// struct MyState {
50///     cli: WasiCliCtx,
51///     table: ResourceTable,
52/// }
53///
54/// impl WasiCliView for MyState {
55///     fn cli(&mut self) -> WasiCliCtxView<'_> {
56///         WasiCliCtxView {
57///             ctx: &mut self.cli,
58///             table: &mut self.table,
59///         }
60///     }
61/// }
62/// ```
63pub fn add_to_linker<T>(linker: &mut Linker<T>) -> wasmtime::Result<()>
64where
65    T: WasiCliView + 'static,
66{
67    exit::add_to_linker::<_, WasiCli>(linker, T::cli)?;
68    environment::add_to_linker::<_, WasiCli>(linker, T::cli)?;
69    stdin::add_to_linker::<_, WasiCli>(linker, T::cli)?;
70    stdout::add_to_linker::<_, WasiCli>(linker, T::cli)?;
71    stderr::add_to_linker::<_, WasiCli>(linker, T::cli)?;
72    terminal_input::add_to_linker::<_, WasiCli>(linker, T::cli)?;
73    terminal_output::add_to_linker::<_, WasiCli>(linker, T::cli)?;
74    terminal_stdin::add_to_linker::<_, WasiCli>(linker, T::cli)?;
75    terminal_stdout::add_to_linker::<_, WasiCli>(linker, T::cli)?;
76    terminal_stderr::add_to_linker::<_, WasiCli>(linker, T::cli)?;
77    Ok(())
78}
79
80/// Convenience function to add `wasi:cli` interfaces into `linker` for any
81/// named imports of `wasi:cli` interfaces.
82///
83/// This function is similar to [`add_to_linker`] except that it's specifically
84/// designed to work with named imports of `wasi:cli` interfaces that components
85/// may have. This requires a [`Component`] parameter to be passed in when
86/// populating the [`Linker`] provided to see what the [`Component`] actually
87/// imports.
88///
89/// Like [`add_to_linker`] this is a bit low level and you may want to possibly
90/// invoke [`wasmtime_wasi::p3::add_named_to_linker`] instead. Alternatively if
91/// this isn't low level enough you can additionally invoke bindgen-generated
92/// `add_to_linker` functions directly from within the
93/// [`named_imports::wasi::cli`] module.
94///
95/// [`wasmtime_wasi::p3::add_named_to_linker`]: crate::p3::add_named_to_linker
96/// [`named_imports::wasi::cli`]: crate::p3::bindings::named_imports::wasi::cli
97///
98/// The `lookup` function provided here is invoked for every named import found
99/// for a particular interface. The [`Interface`] given is what's being bound,
100/// and the `&str` argument is the name that the component imports it as. The
101/// embedder can then decide how it would like to allocate a [`NamedId`] for
102/// this import. If `Ok` is returned then the linker is populated with this
103/// name, and imported functions will pass the [`NamedId`] later to the
104/// implementation of [`WasiCliNamedView`] on `T` when invoked. If `Err` is
105/// returned then the error will cause this entire function to fail and this
106/// function call will return the same error.
107///
108/// # Example
109///
110/// ```
111/// use std::collections::HashMap;
112/// use wasmtime::component::{Component, Linker, ResourceTable};
113/// use wasmtime::{Engine, Result, Store, Config};
114/// use wasmtime_wasi::NamedId;
115/// use wasmtime_wasi::cli::{WasiCliCtx, WasiCliNamedView, WasiCliCtxView};
116///
117/// fn main() -> Result<()> {
118///     let engine = Engine::default();
119///     let component = Component::new(&engine, "(component)")?;
120///
121///     let mut linker = Linker::<MyState>::new(&engine);
122///
123///     // ... add default functionality to `linker` as needed ...
124///
125///     // and then additionally fill in any specific named imports `component`
126///     // might have for `wasi:cli` interfaces.
127///     let mut name_map = HashMap::new();
128///     wasmtime_wasi::p3::cli::add_named_to_linker(&mut linker, &component, |_i, name| {
129///         let len = name_map.len();
130///         Ok(NamedId(*name_map.entry(name.to_string()).or_insert(len)))
131///     })?;
132///
133///     // Here a `WasiCliCtx` is allocated per-named-import and will then be
134///     // referred to internally by the [`NamedId`] allocated above. You could
135///     // also use `name_map` to configure each context differently.
136///     let mut my_state = MyState::default();
137///     for _ in 0..name_map.len() {
138///         my_state.contexts.push(WasiCliCtx::default());
139///     }
140///     let mut store = Store::new(&engine, my_state);
141///     let instance = linker.instantiate(&mut store, &component)?;
142///
143///     // ... work with `instance` ...
144///
145///     Ok(())
146/// }
147///
148/// #[derive(Default)]
149/// struct MyState {
150///     table: ResourceTable,
151///     contexts: Vec<WasiCliCtx>,
152/// }
153///
154/// impl WasiCliNamedView for MyState {
155///     fn cli(&mut self, id: NamedId) -> WasiCliCtxView<'_> {
156///         WasiCliCtxView {
157///             ctx: &mut self.contexts[id.0],
158///             table: &mut self.table,
159///         }
160///     }
161/// }
162/// ```
163pub fn add_named_to_linker<T>(
164    linker: &mut Linker<T>,
165    component: &Component,
166    mut lookup: impl FnMut(Interface, &str) -> wasmtime::Result<NamedId>,
167) -> wasmtime::Result<()>
168where
169    T: WasiCliNamedView + 'static,
170{
171    use crate::p3::bindings::named_imports::wasi::cli::{
172        environment, exit, stderr, stdin, stdout, terminal_input, terminal_output, terminal_stderr,
173        terminal_stdin, terminal_stdout,
174    };
175    exit::add_to_linker::<_, WasiCliNamed<T>>(
176        linker,
177        component,
178        |name| lookup(Interface::CliExit, name),
179        |x| WasiCtxNamedView(x),
180    )?;
181    environment::add_to_linker::<_, WasiCliNamed<T>>(
182        linker,
183        component,
184        |name| lookup(Interface::CliEnvironment, name),
185        |x| WasiCtxNamedView(x),
186    )?;
187    stdin::add_to_linker::<_, WasiCliNamed<T>>(
188        linker,
189        component,
190        |name| lookup(Interface::CliStdin, name),
191        |x| WasiCtxNamedView(x),
192    )?;
193    stdout::add_to_linker::<_, WasiCliNamed<T>>(
194        linker,
195        component,
196        |name| lookup(Interface::CliStdout, name),
197        |x| WasiCtxNamedView(x),
198    )?;
199    stderr::add_to_linker::<_, WasiCliNamed<T>>(
200        linker,
201        component,
202        |name| lookup(Interface::CliStderr, name),
203        |x| WasiCtxNamedView(x),
204    )?;
205    terminal_input::add_to_linker::<_, WasiCliNamed<T>>(
206        linker,
207        component,
208        |name| lookup(Interface::CliTerminalInput, name),
209        |x| WasiCtxNamedView(x),
210    )?;
211    terminal_output::add_to_linker::<_, WasiCliNamed<T>>(
212        linker,
213        component,
214        |name| lookup(Interface::CliTerminalOutput, name),
215        |x| WasiCtxNamedView(x),
216    )?;
217    terminal_stdin::add_to_linker::<_, WasiCliNamed<T>>(
218        linker,
219        component,
220        |name| lookup(Interface::CliTerminalStdin, name),
221        |x| WasiCtxNamedView(x),
222    )?;
223    terminal_stdout::add_to_linker::<_, WasiCliNamed<T>>(
224        linker,
225        component,
226        |name| lookup(Interface::CliTerminalStdout, name),
227        |x| WasiCtxNamedView(x),
228    )?;
229    terminal_stderr::add_to_linker::<_, WasiCliNamed<T>>(
230        linker,
231        component,
232        |name| lookup(Interface::CliTerminalStderr, name),
233        |x| WasiCtxNamedView(x),
234    )?;
235    Ok(())
236}
237
238pub struct TerminalInput;
239pub struct TerminalOutput;