wash_runtime/plugin/
wasi_config.rs1use std::{
21 collections::{HashMap, HashSet},
22 sync::Arc,
23};
24use tokio::sync::RwLock;
25
26use crate::{
27 engine::{ctx::Ctx, workload::WorkloadComponent},
28 plugin::HostPlugin,
29 wit::{WitInterface, WitWorld},
30};
31
32mod bindings {
33 wasmtime::component::bindgen!({
34 world: "config",
35 trappable_imports: true,
36 async: true,
37 });
38}
39
40use bindings::wasi::config::runtime::{ConfigError, Host};
41
42const RUNTIME_CONFIG_ID: &str = "runtime-config";
43
44type ConfigMap = HashMap<Arc<str>, HashMap<String, String>>;
45
46#[derive(Clone, Default)]
52pub struct RuntimeConfig {
53 config: Arc<RwLock<ConfigMap>>,
55}
56
57impl Host for Ctx {
58 async fn get(&mut self, key: String) -> anyhow::Result<Result<Option<String>, ConfigError>> {
59 let Some(plugin) = self.get_plugin::<RuntimeConfig>(RUNTIME_CONFIG_ID) else {
60 return Ok(Ok(None));
61 };
62 let config_guard = plugin.config.read().await;
63 config_guard
64 .get(&*self.component_id)
65 .and_then(|map| map.get(&key).cloned())
66 .map_or(Ok(Ok(None)), |v| Ok(Ok(Some(v))))
67 }
68
69 async fn get_all(&mut self) -> anyhow::Result<Result<Vec<(String, String)>, ConfigError>> {
70 let Some(plugin) = self.get_plugin::<RuntimeConfig>(RUNTIME_CONFIG_ID) else {
71 return Ok(Ok(vec![]));
72 };
73 let config_guard = plugin.config.read().await;
74 let entries = config_guard
75 .get(&*self.component_id)
76 .map(|map| map.iter().map(|(k, v)| (k.clone(), v.clone())).collect())
77 .unwrap_or_default();
78 Ok(Ok(entries))
79 }
80}
81
82#[async_trait::async_trait]
83impl HostPlugin for RuntimeConfig {
84 fn id(&self) -> &'static str {
85 RUNTIME_CONFIG_ID
86 }
87
88 fn world(&self) -> WitWorld {
89 WitWorld {
90 imports: HashSet::from([WitInterface::from("wasi:config/runtime@0.2.0-draft")]),
91 exports: HashSet::new(),
92 }
93 }
94 async fn on_component_bind(
95 &self,
96 component_handle: &mut WorkloadComponent,
97 interfaces: std::collections::HashSet<crate::wit::WitInterface>,
98 ) -> anyhow::Result<()> {
99 let Some(interface) = interfaces.iter().find(|i| {
101 i.namespace == "wasi" && i.package == "config" && i.interfaces.contains("runtime")
102 }) else {
103 tracing::warn!(
105 "RuntimeConfig plugin requested for non-wasi:config/runtime interface(s): {:?}",
106 interfaces
107 );
108 return Ok(());
109 };
110
111 bindings::wasi::config::runtime::add_to_linker(component_handle.linker(), |ctx| ctx)?;
113
114 self.config
116 .write()
117 .await
118 .insert(Arc::from(component_handle.id()), interface.config.clone());
119
120 Ok(())
121 }
122}