wasmtime_wasi_config/lib.rs
1//! # Wasmtime's [wasi-config] Implementation
2//!
3//! This crate provides a Wasmtime host implementation of the [wasi-config]
4//! API. With this crate, the runtime can run components that call APIs in
5//! [wasi-config] and provide configuration variables for the component.
6//!
7//! # Examples
8//!
9//! The usage of this crate is very similar to other WASI API implementations
10//! such as [wasi:cli] and [wasi:http].
11//!
12//! A common scenario is getting runtime-passed configurations in a [wasi:cli]
13//! component. A standalone example of doing all this looks like:
14//!
15//! ```
16//! use wasmtime::{
17//! component::{Linker, ResourceTable},
18//! Config, Engine, Result, Store,
19//! };
20//! use wasmtime_wasi::{IoView, WasiCtx, WasiCtxBuilder, WasiView};
21//! use wasmtime_wasi_config::{WasiConfig, WasiConfigVariables};
22//!
23//! #[tokio::main]
24//! async fn main() -> Result<()> {
25//! let mut config = Config::new();
26//! config.async_support(true);
27//! let engine = Engine::new(&config)?;
28//!
29//! let mut store = Store::new(&engine, Ctx {
30//! table: ResourceTable::new(),
31//! wasi_ctx: WasiCtxBuilder::new().build(),
32//! wasi_config_vars: WasiConfigVariables::from_iter(vec![
33//! ("config_key1", "value1"),
34//! ("config_key2", "value2"),
35//! ]),
36//! });
37//!
38//! let mut linker = Linker::<Ctx>::new(&engine);
39//! wasmtime_wasi::add_to_linker_async(&mut linker)?;
40//! // add `wasi-config` world's interfaces to the linker
41//! wasmtime_wasi_config::add_to_linker(&mut linker, |h: &mut Ctx| {
42//! WasiConfig::from(&h.wasi_config_vars)
43//! })?;
44//!
45//! // ... use `linker` to instantiate within `store` ...
46//!
47//! Ok(())
48//! }
49//!
50//! struct Ctx {
51//! table: ResourceTable,
52//! wasi_ctx: WasiCtx,
53//! wasi_config_vars: WasiConfigVariables,
54//! }
55//!
56//! impl IoView for Ctx {
57//! fn table(&mut self) -> &mut ResourceTable { &mut self.table }
58//! }
59//! impl WasiView for Ctx {
60//! fn ctx(&mut self) -> &mut WasiCtx { &mut self.wasi_ctx }
61//! }
62//! ```
63//!
64//! [wasi-config]: https://github.com/WebAssembly/wasi-config
65//! [wasi:cli]: https://docs.rs/wasmtime-wasi/latest
66//! [wasi:http]: https://docs.rs/wasmtime-wasi-http/latest
67
68#![deny(missing_docs)]
69
70use anyhow::Result;
71use std::collections::HashMap;
72
73mod gen_ {
74 wasmtime::component::bindgen!({
75 path: "wit",
76 world: "wasi:config/imports",
77 trappable_imports: true,
78 });
79}
80use self::gen_::wasi::config::store as generated;
81
82/// Capture the state necessary for use in the `wasi-config` API implementation.
83#[derive(Default)]
84pub struct WasiConfigVariables(HashMap<String, String>);
85
86impl<S: Into<String>> FromIterator<(S, S)> for WasiConfigVariables {
87 fn from_iter<I: IntoIterator<Item = (S, S)>>(iter: I) -> Self {
88 Self(
89 iter.into_iter()
90 .map(|(k, v)| (k.into(), v.into()))
91 .collect(),
92 )
93 }
94}
95
96impl WasiConfigVariables {
97 /// Create a new runtime configuration.
98 pub fn new() -> Self {
99 Default::default()
100 }
101
102 /// Insert a key-value pair into the configuration map.
103 pub fn insert(&mut self, key: impl Into<String>, value: impl Into<String>) -> &mut Self {
104 self.0.insert(key.into(), value.into());
105 self
106 }
107}
108
109/// A wrapper capturing the needed internal `wasi-config` state.
110pub struct WasiConfig<'a> {
111 vars: &'a WasiConfigVariables,
112}
113
114impl<'a> From<&'a WasiConfigVariables> for WasiConfig<'a> {
115 fn from(vars: &'a WasiConfigVariables) -> Self {
116 Self { vars }
117 }
118}
119
120impl<'a> WasiConfig<'a> {
121 /// Create a new view into the `wasi-config` state.
122 pub fn new(vars: &'a WasiConfigVariables) -> Self {
123 Self { vars }
124 }
125}
126
127impl generated::Host for WasiConfig<'_> {
128 fn get(&mut self, key: String) -> Result<Result<Option<String>, generated::Error>> {
129 Ok(Ok(self.vars.0.get(&key).map(|s| s.to_owned())))
130 }
131
132 fn get_all(&mut self) -> Result<Result<Vec<(String, String)>, generated::Error>> {
133 Ok(Ok(self
134 .vars
135 .0
136 .iter()
137 .map(|(k, v)| (k.to_string(), v.to_string()))
138 .collect()))
139 }
140}
141
142/// Add all the `wasi-config` world's interfaces to a [`wasmtime::component::Linker`].
143pub fn add_to_linker<T>(
144 l: &mut wasmtime::component::Linker<T>,
145 f: impl Fn(&mut T) -> WasiConfig<'_> + Send + Sync + Copy + 'static,
146) -> Result<()> {
147 generated::add_to_linker_get_host(l, f)?;
148 Ok(())
149}