Skip to main content

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//!     Engine, Result, Store,
19//! };
20//! use wasmtime_wasi::{WasiCtx, WasiCtxView, WasiView};
21//! use wasmtime_wasi_config::{WasiConfig, WasiConfigVariables};
22//!
23//! #[tokio::main]
24//! async fn main() -> Result<()> {
25//!     let engine = Engine::default();
26//!
27//!     let mut store = Store::new(&engine, Ctx {
28//!         table: ResourceTable::new(),
29//!         wasi_ctx: WasiCtx::builder().build(),
30//!         wasi_config_vars: WasiConfigVariables::from_iter(vec![
31//!             ("config_key1", "value1"),
32//!             ("config_key2", "value2"),
33//!         ]),
34//!     });
35//!
36//!     let mut linker = Linker::<Ctx>::new(&engine);
37//!     wasmtime_wasi::p2::add_to_linker_async(&mut linker)?;
38//!     // add `wasi-config` world's interfaces to the linker
39//!     wasmtime_wasi_config::add_to_linker(&mut linker, |h: &mut Ctx| {
40//!         WasiConfig::from(&h.wasi_config_vars)
41//!     })?;
42//!
43//!     // ... use `linker` to instantiate within `store` ...
44//!
45//!     Ok(())
46//! }
47//!
48//! struct Ctx {
49//!     table: ResourceTable,
50//!     wasi_ctx: WasiCtx,
51//!     wasi_config_vars: WasiConfigVariables,
52//! }
53//!
54//! impl WasiView for Ctx {
55//!     fn ctx(&mut self) -> WasiCtxView<'_> {
56//!         WasiCtxView { ctx: &mut self.wasi_ctx, table: &mut self.table }
57//!     }
58//! }
59//! ```
60//!
61//! [wasi-config]: https://github.com/WebAssembly/wasi-config
62//! [wasi:cli]: https://docs.rs/wasmtime-wasi/latest
63//! [wasi:http]: https://docs.rs/wasmtime-wasi-http/latest
64
65#![deny(missing_docs)]
66
67use std::collections::HashMap;
68use wasmtime::Result;
69use wasmtime::component::HasData;
70
71mod gen_ {
72    wasmtime::component::bindgen!({
73        path: "wit",
74        world: "wasi:config/imports",
75        imports: { default: trappable },
76    });
77}
78use self::gen_::wasi::config::store as generated;
79
80/// Capture the state necessary for use in the `wasi-config` API implementation.
81#[derive(Default)]
82pub struct WasiConfigVariables(HashMap<String, String>);
83
84impl<S: Into<String>> FromIterator<(S, S)> for WasiConfigVariables {
85    fn from_iter<I: IntoIterator<Item = (S, S)>>(iter: I) -> Self {
86        Self(
87            iter.into_iter()
88                .map(|(k, v)| (k.into(), v.into()))
89                .collect(),
90        )
91    }
92}
93
94impl WasiConfigVariables {
95    /// Create a new runtime configuration.
96    pub fn new() -> Self {
97        Default::default()
98    }
99
100    /// Insert a key-value pair into the configuration map.
101    pub fn insert(&mut self, key: impl Into<String>, value: impl Into<String>) -> &mut Self {
102        self.0.insert(key.into(), value.into());
103        self
104    }
105}
106
107/// A wrapper capturing the needed internal `wasi-config` state.
108pub struct WasiConfig<'a> {
109    vars: &'a WasiConfigVariables,
110}
111
112impl<'a> From<&'a WasiConfigVariables> for WasiConfig<'a> {
113    fn from(vars: &'a WasiConfigVariables) -> Self {
114        Self { vars }
115    }
116}
117
118impl<'a> WasiConfig<'a> {
119    /// Create a new view into the `wasi-config` state.
120    pub fn new(vars: &'a WasiConfigVariables) -> Self {
121        Self { vars }
122    }
123}
124
125impl generated::Host for WasiConfig<'_> {
126    fn get(&mut self, key: String) -> Result<Result<Option<String>, generated::Error>> {
127        Ok(Ok(self.vars.0.get(&key).map(|s| s.to_owned())))
128    }
129
130    fn get_all(&mut self) -> Result<Result<Vec<(String, String)>, generated::Error>> {
131        Ok(Ok(self
132            .vars
133            .0
134            .iter()
135            .map(|(k, v)| (k.to_string(), v.to_string()))
136            .collect()))
137    }
138}
139
140/// Add all the `wasi-config` world's interfaces to a [`wasmtime::component::Linker`].
141pub fn add_to_linker<T: 'static>(
142    l: &mut wasmtime::component::Linker<T>,
143    f: fn(&mut T) -> WasiConfig<'_>,
144) -> Result<()> {
145    generated::add_to_linker::<T, HasWasiConfig>(l, f)?;
146    Ok(())
147}
148
149struct HasWasiConfig;
150
151impl HasData for HasWasiConfig {
152    type Data<'a> = WasiConfig<'a>;
153}