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