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::p2::{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::p2::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;
72use wasmtime::component::HasData;
73
74mod gen_ {
75    wasmtime::component::bindgen!({
76        path: "wit",
77        world: "wasi:config/imports",
78        trappable_imports: true,
79    });
80}
81use self::gen_::wasi::config::store as generated;
82
83/// Capture the state necessary for use in the `wasi-config` API implementation.
84#[derive(Default)]
85pub struct WasiConfigVariables(HashMap<String, String>);
86
87impl<S: Into<String>> FromIterator<(S, S)> for WasiConfigVariables {
88    fn from_iter<I: IntoIterator<Item = (S, S)>>(iter: I) -> Self {
89        Self(
90            iter.into_iter()
91                .map(|(k, v)| (k.into(), v.into()))
92                .collect(),
93        )
94    }
95}
96
97impl WasiConfigVariables {
98    /// Create a new runtime configuration.
99    pub fn new() -> Self {
100        Default::default()
101    }
102
103    /// Insert a key-value pair into the configuration map.
104    pub fn insert(&mut self, key: impl Into<String>, value: impl Into<String>) -> &mut Self {
105        self.0.insert(key.into(), value.into());
106        self
107    }
108}
109
110/// A wrapper capturing the needed internal `wasi-config` state.
111pub struct WasiConfig<'a> {
112    vars: &'a WasiConfigVariables,
113}
114
115impl<'a> From<&'a WasiConfigVariables> for WasiConfig<'a> {
116    fn from(vars: &'a WasiConfigVariables) -> Self {
117        Self { vars }
118    }
119}
120
121impl<'a> WasiConfig<'a> {
122    /// Create a new view into the `wasi-config` state.
123    pub fn new(vars: &'a WasiConfigVariables) -> Self {
124        Self { vars }
125    }
126}
127
128impl generated::Host for WasiConfig<'_> {
129    fn get(&mut self, key: String) -> Result<Result<Option<String>, generated::Error>> {
130        Ok(Ok(self.vars.0.get(&key).map(|s| s.to_owned())))
131    }
132
133    fn get_all(&mut self) -> Result<Result<Vec<(String, String)>, generated::Error>> {
134        Ok(Ok(self
135            .vars
136            .0
137            .iter()
138            .map(|(k, v)| (k.to_string(), v.to_string()))
139            .collect()))
140    }
141}
142
143/// Add all the `wasi-config` world's interfaces to a [`wasmtime::component::Linker`].
144pub fn add_to_linker<T: 'static>(
145    l: &mut wasmtime::component::Linker<T>,
146    f: fn(&mut T) -> WasiConfig<'_>,
147) -> Result<()> {
148    generated::add_to_linker::<T, HasWasiConfig>(l, f)?;
149    Ok(())
150}
151
152struct HasWasiConfig;
153
154impl HasData for HasWasiConfig {
155    type Data<'a> = WasiConfig<'a>;
156}