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 
67 use std::collections::HashMap;
68 use wasmtime::Result;
69 use wasmtime::component::HasData;
70 
71 mod gen_ {
72     wasmtime::component::bindgen!({
73         path: "wit",
74         world: "wasi:config/imports",
75         imports: { default: trappable },
76     });
77 }
78 use self::gen_::wasi::config::store as generated;
79 
80 /// Capture the state necessary for use in the `wasi-config` API implementation.
81 #[derive(Default)]
82 pub struct WasiConfigVariables(HashMap<String, String>);
83 
84 impl<S: Into<String>> FromIterator<(S, S)> for WasiConfigVariables {
from_iter<I: IntoIterator<Item = (S, S)>>(iter: I) -> Self85     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 
94 impl WasiConfigVariables {
95     /// Create a new runtime configuration.
new() -> Self96     pub fn new() -> Self {
97         Default::default()
98     }
99 
100     /// Insert a key-value pair into the configuration map.
insert(&mut self, key: impl Into<String>, value: impl Into<String>) -> &mut Self101     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.
108 pub struct WasiConfig<'a> {
109     vars: &'a WasiConfigVariables,
110 }
111 
112 impl<'a> From<&'a WasiConfigVariables> for WasiConfig<'a> {
from(vars: &'a WasiConfigVariables) -> Self113     fn from(vars: &'a WasiConfigVariables) -> Self {
114         Self { vars }
115     }
116 }
117 
118 impl<'a> WasiConfig<'a> {
119     /// Create a new view into the `wasi-config` state.
new(vars: &'a WasiConfigVariables) -> Self120     pub fn new(vars: &'a WasiConfigVariables) -> Self {
121         Self { vars }
122     }
123 }
124 
125 impl generated::Host for WasiConfig<'_> {
get(&mut self, key: String) -> Result<Result<Option<String>, generated::Error>>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 
get_all(&mut self) -> Result<Result<Vec<(String, String)>, generated::Error>>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`].
add_to_linker<T: 'static>( l: &mut wasmtime::component::Linker<T>, f: fn(&mut T) -> WasiConfig<'_>, ) -> Result<()>141 pub 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 
149 struct HasWasiConfig;
150 
151 impl HasData for HasWasiConfig {
152     type Data<'a> = WasiConfig<'a>;
153 }
154