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::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}