capsule_core/wasm/
runtime.rs1use 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}