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}