Skip to main content

wash_runtime/engine/
ctx.rs

1//! Component execution context for wasmtime stores.
2//!
3//! This module provides the [`Ctx`] type which serves as the store context
4//! for wasmtime when executing WebAssembly components. It integrates WASI
5//! interfaces, HTTP capabilities, and plugin access into a unified context.
6
7use std::{any::Any, collections::HashMap, sync::Arc};
8
9use wasmtime::component::ResourceTable;
10use wasmtime_wasi::{IoView, WasiCtx, WasiCtxBuilder, WasiView};
11use wasmtime_wasi_http::{WasiHttpCtx, WasiHttpView};
12
13use crate::plugin::HostPlugin;
14
15/// The context for a component store and linker, providing access to implementations of:
16/// - wasi@0.2 interfaces
17/// - wasi:http@0.2 interfaces
18pub struct Ctx {
19    /// Unique identifier for this component context. This is a [uuid::Uuid::new_v4] string.
20    pub id: String,
21    /// The unique identifier for the workload component this instance belongs to
22    pub component_id: Arc<str>,
23    /// The unique identifier for the workload this component belongs to
24    pub workload_id: Arc<str>,
25    /// The resource table used to manage resources in the Wasmtime store.
26    pub table: wasmtime::component::ResourceTable,
27    /// The WASI context used to provide WASI functionality to the components using this context.
28    pub ctx: WasiCtx,
29    /// The HTTP context used to provide HTTP functionality to the component.
30    pub http: WasiHttpCtx,
31    /// Plugin instances stored by string ID for access during component execution.
32    /// These all implement the [`HostPlugin`] trait, but they are cast as `Arc<dyn Any + Send + Sync>`
33    /// to support downcasting to the specific plugin type in [`Ctx::get_plugin`]
34    plugins: HashMap<&'static str, Arc<dyn Any + Send + Sync>>,
35}
36
37impl Ctx {
38    /// Get a plugin by its string ID and downcast to the expected type
39    pub fn get_plugin<T: HostPlugin + 'static>(&self, plugin_id: &str) -> Option<Arc<T>> {
40        self.plugins.get(plugin_id)?.clone().downcast().ok()
41    }
42
43    /// Create a new [`CtxBuilder`] to construct a [`Ctx`]
44    pub fn builder(
45        workload_id: impl Into<Arc<str>>,
46        component_id: impl Into<Arc<str>>,
47    ) -> CtxBuilder {
48        CtxBuilder::new(workload_id, component_id)
49    }
50}
51
52impl std::fmt::Debug for Ctx {
53    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54        f.debug_struct("Ctx")
55            .field("id", &self.id)
56            .field("workload_id", &self.workload_id.as_ref())
57            .field("table", &self.table)
58            .finish()
59    }
60}
61
62impl IoView for Ctx {
63    fn table(&mut self) -> &mut ResourceTable {
64        &mut self.table
65    }
66}
67// TODO: Do some cleverness to pull up the _right_ WasiCtx based on what component is active, maybe
68impl WasiView for Ctx {
69    fn ctx(&mut self) -> &mut WasiCtx {
70        &mut self.ctx
71    }
72}
73
74// Implement WasiHttpView for wasi:http@0.2
75impl WasiHttpView for Ctx {
76    fn ctx(&mut self) -> &mut WasiHttpCtx {
77        &mut self.http
78    }
79}
80
81/// Helper struct to build a [`Ctx`] with a builder pattern
82pub struct CtxBuilder {
83    id: String,
84    workload_id: Arc<str>,
85    component_id: Arc<str>,
86    ctx: Option<WasiCtx>,
87    plugins: HashMap<&'static str, Arc<dyn HostPlugin + Send + Sync>>,
88}
89
90impl CtxBuilder {
91    pub fn new(workload_id: impl Into<Arc<str>>, component_id: impl Into<Arc<str>>) -> Self {
92        Self {
93            id: uuid::Uuid::new_v4().to_string(),
94            component_id: component_id.into(),
95            workload_id: workload_id.into(),
96            ctx: None,
97            plugins: HashMap::new(),
98        }
99    }
100
101    pub fn with_wasi_ctx(mut self, ctx: WasiCtx) -> Self {
102        self.ctx = Some(ctx);
103        self
104    }
105
106    pub fn with_plugins(
107        mut self,
108        plugins: HashMap<&'static str, Arc<dyn HostPlugin + Send + Sync>>,
109    ) -> Self {
110        self.plugins.extend(plugins);
111        self
112    }
113
114    pub fn build(self) -> Ctx {
115        let plugins = self
116            .plugins
117            .into_iter()
118            .map(|(k, v)| (k, v as Arc<dyn Any + Send + Sync>))
119            .collect();
120
121        Ctx {
122            id: self.id,
123            ctx: self.ctx.unwrap_or_else(|| {
124                WasiCtxBuilder::new()
125                    .args(&["main.wasm"])
126                    .inherit_stderr()
127                    .build()
128            }),
129            workload_id: self.workload_id,
130            component_id: self.component_id,
131            http: WasiHttpCtx::new(),
132            table: ResourceTable::new(),
133            plugins,
134        }
135    }
136}