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;