Skip to main content

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