astrid_plugins/wasm/
loader.rs1use std::sync::Arc;
7use std::time::Duration;
8
9use crate::manifest::PluginManifest;
10use crate::security::PluginSecurityGate;
11use crate::wasm::plugin::{WasmPlugin, WasmPluginConfig};
12
13const DEFAULT_MAX_MEMORY_BYTES: u64 = 64 * 1024 * 1024;
15
16const DEFAULT_MAX_EXECUTION_TIME: Duration = Duration::from_secs(30);
18
19pub struct WasmPluginLoader {
32 security: Option<Arc<dyn PluginSecurityGate>>,
33 max_memory_bytes: u64,
34 max_execution_time: Duration,
35 require_hash: bool,
36}
37
38impl std::fmt::Debug for WasmPluginLoader {
39 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40 f.debug_struct("WasmPluginLoader")
41 .field("has_security", &self.security.is_some())
42 .field("max_memory_bytes", &self.max_memory_bytes)
43 .field("max_execution_time", &self.max_execution_time)
44 .field("require_hash", &self.require_hash)
45 .finish()
46 }
47}
48
49impl Default for WasmPluginLoader {
50 fn default() -> Self {
51 Self::new()
52 }
53}
54
55impl WasmPluginLoader {
56 #[must_use]
58 pub fn new() -> Self {
59 Self {
60 security: None,
61 max_memory_bytes: DEFAULT_MAX_MEMORY_BYTES,
62 max_execution_time: DEFAULT_MAX_EXECUTION_TIME,
63 require_hash: false,
64 }
65 }
66
67 #[must_use]
69 pub fn with_security(mut self, gate: Arc<dyn PluginSecurityGate>) -> Self {
70 self.security = Some(gate);
71 self
72 }
73
74 #[must_use]
76 pub fn with_memory_limit(mut self, bytes: u64) -> Self {
77 self.max_memory_bytes = bytes;
78 self
79 }
80
81 #[must_use]
83 pub fn with_timeout(mut self, duration: Duration) -> Self {
84 self.max_execution_time = duration;
85 self
86 }
87
88 #[must_use]
93 pub fn with_require_hash(mut self, require: bool) -> Self {
94 self.require_hash = require;
95 self
96 }
97
98 #[must_use]
103 pub fn create_plugin(&self, manifest: PluginManifest) -> WasmPlugin {
104 let config = WasmPluginConfig {
105 security: self.security.clone(),
106 max_memory_bytes: self.max_memory_bytes,
107 max_execution_time: self.max_execution_time,
108 require_hash: self.require_hash,
109 };
110 WasmPlugin::new(manifest, config)
111 }
112
113 #[must_use]
115 pub fn max_memory_bytes(&self) -> u64 {
116 self.max_memory_bytes
117 }
118
119 #[must_use]
121 pub fn max_execution_time(&self) -> Duration {
122 self.max_execution_time
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 #[test]
131 fn default_config() {
132 let loader = WasmPluginLoader::new();
133 assert_eq!(loader.max_memory_bytes(), 64 * 1024 * 1024);
134 assert_eq!(loader.max_execution_time(), Duration::from_secs(30));
135 }
136
137 #[test]
138 fn custom_config() {
139 let loader = WasmPluginLoader::new()
140 .with_memory_limit(32 * 1024 * 1024)
141 .with_timeout(Duration::from_secs(10));
142 assert_eq!(loader.max_memory_bytes(), 32 * 1024 * 1024);
143 assert_eq!(loader.max_execution_time(), Duration::from_secs(10));
144 }
145
146 #[test]
147 fn create_plugin_returns_unloaded() {
148 use crate::Plugin;
149 use crate::manifest::{PluginEntryPoint, PluginManifest};
150 use crate::plugin::PluginState;
151 use std::collections::HashMap;
152 use std::path::PathBuf;
153
154 let loader = WasmPluginLoader::new();
155 let manifest = PluginManifest {
156 id: crate::PluginId::from_static("test"),
157 name: "Test".into(),
158 version: "0.1.0".into(),
159 description: None,
160 author: None,
161 entry_point: PluginEntryPoint::Wasm {
162 path: PathBuf::from("test.wasm"),
163 hash: None,
164 },
165 capabilities: vec![],
166 config: HashMap::new(),
167 };
168 let plugin = loader.create_plugin(manifest);
169 assert_eq!(plugin.state(), PluginState::Unloaded);
170 }
171}