dictator_core/
wasm_cache.rs1use anyhow::{Context, Result};
4use dashmap::DashMap;
5use std::path::{Path, PathBuf};
6use std::sync::Arc;
7use wasmtime::component::Component;
8use wasmtime::{Config, Engine};
9
10pub struct WasmCache {
12 engine: Engine,
13 components: DashMap<PathBuf, Arc<Component>>,
14}
15
16impl WasmCache {
17 pub fn new() -> Result<Self> {
19 let mut config = Config::new();
20 config.wasm_component_model(true);
21 let engine = Engine::new(&config)?;
22
23 Ok(Self {
24 engine,
25 components: DashMap::new(),
26 })
27 }
28
29 pub fn get_or_load(&self, path: &Path) -> Result<Arc<Component>> {
31 if let Some(component) = self.components.get(path) {
33 return Ok::<Arc<Component>, anyhow::Error>(component.clone());
34 }
35
36 let component = Component::from_file(&self.engine, path)
38 .with_context(|| format!("failed to load wasm decree: {}", path.display()))?;
39
40 let component = Arc::new(component);
41
42 self.components
44 .insert(path.to_path_buf(), component.clone());
45
46 tracing::info!("Cached WASM component: {}", path.display());
47 Ok(component)
48 }
49
50 pub fn clear(&self) {
52 self.components.clear();
53 tracing::info!("Cleared WASM component cache");
54 }
55
56 #[must_use]
58 pub fn stats(&self) -> WasmCacheStats {
59 WasmCacheStats {
60 entries: self.components.len(),
61 }
62 }
63}
64
65impl Default for WasmCache {
66 fn default() -> Self {
67 Self::new().expect("Failed to create WASM cache")
68 }
69}
70
71#[derive(Debug, Clone)]
73pub struct WasmCacheStats {
74 pub entries: usize,
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80 use std::io::Write;
81 use tempfile::NamedTempFile;
82
83 #[test]
84 fn test_cache_miss_then_hit() {
85 let cache = WasmCache::new().unwrap();
86
87 let mut temp_file = NamedTempFile::new().unwrap();
89 writeln!(temp_file, "dummy wasm content").unwrap();
90 let path = temp_file.path();
91
92 let result = cache.get_or_load(path);
94 assert!(result.is_err());
95
96 let stats = cache.stats();
98 assert_eq!(stats.entries, 0); }
100
101 #[test]
102 fn test_cache_clear() {
103 let cache = WasmCache::new().unwrap();
104
105 cache.clear();
107 let stats = cache.stats();
108 assert_eq!(stats.entries, 0);
109 }
110}