use std::{
collections::{HashMap, HashSet},
sync::Arc,
};
use tokio::sync::RwLock;
use crate::{
engine::{ctx::Ctx, workload::WorkloadComponent},
plugin::HostPlugin,
wit::{WitInterface, WitWorld},
};
mod bindings {
wasmtime::component::bindgen!({
world: "config",
trappable_imports: true,
async: true,
});
}
use bindings::wasi::config::runtime::{ConfigError, Host};
const RUNTIME_CONFIG_ID: &str = "runtime-config";
type ConfigMap = HashMap<Arc<str>, HashMap<String, String>>;
#[derive(Clone, Default)]
pub struct RuntimeConfig {
config: Arc<RwLock<ConfigMap>>,
}
impl Host for Ctx {
async fn get(&mut self, key: String) -> anyhow::Result<Result<Option<String>, ConfigError>> {
let Some(plugin) = self.get_plugin::<RuntimeConfig>(RUNTIME_CONFIG_ID) else {
return Ok(Ok(None));
};
let config_guard = plugin.config.read().await;
config_guard
.get(&*self.component_id)
.and_then(|map| map.get(&key).cloned())
.map_or(Ok(Ok(None)), |v| Ok(Ok(Some(v))))
}
async fn get_all(&mut self) -> anyhow::Result<Result<Vec<(String, String)>, ConfigError>> {
let Some(plugin) = self.get_plugin::<RuntimeConfig>(RUNTIME_CONFIG_ID) else {
return Ok(Ok(vec![]));
};
let config_guard = plugin.config.read().await;
let entries = config_guard
.get(&*self.component_id)
.map(|map| map.iter().map(|(k, v)| (k.clone(), v.clone())).collect())
.unwrap_or_default();
Ok(Ok(entries))
}
}
#[async_trait::async_trait]
impl HostPlugin for RuntimeConfig {
fn id(&self) -> &'static str {
RUNTIME_CONFIG_ID
}
fn world(&self) -> WitWorld {
WitWorld {
imports: HashSet::from([WitInterface::from("wasi:config/runtime@0.2.0-draft")]),
exports: HashSet::new(),
}
}
async fn on_component_bind(
&self,
component_handle: &mut WorkloadComponent,
interfaces: std::collections::HashSet<crate::wit::WitInterface>,
) -> anyhow::Result<()> {
let Some(interface) = interfaces.iter().find(|i| {
i.namespace == "wasi" && i.package == "config" && i.interfaces.contains("runtime")
}) else {
tracing::warn!(
"RuntimeConfig plugin requested for non-wasi:config/runtime interface(s): {:?}",
interfaces
);
return Ok(());
};
bindings::wasi::config::runtime::add_to_linker(component_handle.linker(), |ctx| ctx)?;
self.config
.write()
.await
.insert(Arc::from(component_handle.id()), interface.config.clone());
Ok(())
}
}