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