capsule_core/wasm/
runtime.rs

1use std::fmt;
2use std::path::PathBuf;
3use std::sync::Arc;
4
5use tokio::sync::{Mutex, RwLock};
6use wasmtime::component::Component;
7use wasmtime::{Config, Engine};
8
9use crate::config::log::{Log, LogError};
10use crate::wasm::utilities::task_reporter::TaskReporter;
11
12pub enum WasmRuntimeError {
13    WasmtimeError(wasmtime::Error),
14    LogError(LogError),
15    ConfigError(String),
16    Timeout,
17}
18
19impl fmt::Display for WasmRuntimeError {
20    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21        match self {
22            WasmRuntimeError::WasmtimeError(msg) => {
23                write!(f, "Runtime error > Wasmtime error > {}", msg)
24            }
25            WasmRuntimeError::LogError(msg) => write!(f, "Runtime error > {}", msg),
26            WasmRuntimeError::ConfigError(msg) => write!(f, "Runtime error > Config > {}", msg),
27            WasmRuntimeError::Timeout => write!(f, "Timed out"),
28        }
29    }
30}
31
32impl From<wasmtime::Error> for WasmRuntimeError {
33    fn from(err: wasmtime::Error) -> Self {
34        WasmRuntimeError::WasmtimeError(err)
35    }
36}
37
38impl From<LogError> for WasmRuntimeError {
39    fn from(err: LogError) -> Self {
40        WasmRuntimeError::LogError(err)
41    }
42}
43
44pub trait RuntimeCommand {
45    type Output;
46    fn execute(
47        self,
48        runtime: Arc<Runtime>,
49    ) -> impl Future<Output = Result<Self::Output, WasmRuntimeError>> + Send;
50}
51
52pub struct RuntimeConfig {
53    pub cache_dir: PathBuf,
54    pub verbose: bool,
55}
56
57impl Default for RuntimeConfig {
58    fn default() -> Self {
59        Self {
60            cache_dir: PathBuf::from(".capsule"),
61            verbose: false,
62        }
63    }
64}
65
66pub struct Runtime {
67    pub(crate) engine: Engine,
68    pub(crate) log: Log,
69
70    #[allow(dead_code)]
71    pub(crate) cache_dir: PathBuf,
72
73    pub verbose: bool,
74
75    component: RwLock<Option<Component>>,
76    pub task_reporter: Arc<Mutex<TaskReporter>>,
77}
78
79impl Runtime {
80    pub fn new() -> Result<Arc<Self>, WasmRuntimeError> {
81        Self::with_config(RuntimeConfig::default())
82    }
83
84    pub fn with_config(config: RuntimeConfig) -> Result<Arc<Self>, WasmRuntimeError> {
85        let mut engine_config = Config::new();
86        let db_path = config.cache_dir.join("trace.db");
87        let log = Log::new(
88            Some(
89                db_path
90                    .parent()
91                    .expect("Cache dir is empty")
92                    .to_str()
93                    .expect("failed to get cache dir"),
94            ),
95            db_path
96                .file_name()
97                .expect("Cache dir is empty")
98                .to_str()
99                .expect("failed to get cache dir"),
100        )?;
101
102        engine_config.wasm_component_model(true);
103        engine_config.async_support(true);
104        engine_config.consume_fuel(true);
105
106        let task_reporter = Arc::new(Mutex::new(TaskReporter::new(config.verbose)));
107
108        Ok(Arc::new(Self {
109            engine: Engine::new(&engine_config)?,
110            log,
111            cache_dir: config.cache_dir,
112            verbose: config.verbose,
113            component: RwLock::new(None),
114            task_reporter,
115        }))
116    }
117
118    pub async fn execute<C: RuntimeCommand>(
119        self: &Arc<Self>,
120        command: C,
121    ) -> Result<C::Output, WasmRuntimeError> {
122        command.execute(Arc::clone(self)).await
123    }
124
125    pub async fn get_component(&self) -> Option<Component> {
126        self.component.read().await.clone()
127    }
128
129    pub async fn set_component(&self, component: Component) {
130        *self.component.write().await = Some(component);
131    }
132}