1use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7use std::fmt;
8use thiserror::Error;
9
10#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
12pub enum WasmValue {
13 I32(i32),
15 I64(i64),
17 F32(f32),
19 F64(f64),
21 Bool(bool),
23 String(String),
25 Bytes(Vec<u8>),
27 Null,
29 Array(Vec<WasmValue>),
31 Map(HashMap<String, WasmValue>),
33}
34
35impl WasmValue {
36 pub fn as_i32(&self) -> Option<i32> {
37 match self {
38 WasmValue::I32(v) => Some(*v),
39 WasmValue::I64(v) => Some(*v as i32),
40 _ => None,
41 }
42 }
43
44 pub fn as_i64(&self) -> Option<i64> {
45 match self {
46 WasmValue::I32(v) => Some(*v as i64),
47 WasmValue::I64(v) => Some(*v),
48 _ => None,
49 }
50 }
51
52 pub fn as_f32(&self) -> Option<f32> {
53 match self {
54 WasmValue::F32(v) => Some(*v),
55 WasmValue::F64(v) => Some(*v as f32),
56 _ => None,
57 }
58 }
59
60 pub fn as_f64(&self) -> Option<f64> {
61 match self {
62 WasmValue::F32(v) => Some(*v as f64),
63 WasmValue::F64(v) => Some(*v),
64 _ => None,
65 }
66 }
67
68 pub fn as_bool(&self) -> Option<bool> {
69 match self {
70 WasmValue::Bool(v) => Some(*v),
71 WasmValue::I32(v) => Some(*v != 0),
72 _ => None,
73 }
74 }
75
76 pub fn as_string(&self) -> Option<&str> {
77 match self {
78 WasmValue::String(s) => Some(s),
79 _ => None,
80 }
81 }
82
83 pub fn as_bytes(&self) -> Option<&[u8]> {
84 match self {
85 WasmValue::Bytes(b) => Some(b),
86 _ => None,
87 }
88 }
89
90 pub fn is_null(&self) -> bool {
91 matches!(self, WasmValue::Null)
92 }
93}
94
95impl From<i32> for WasmValue {
96 fn from(v: i32) -> Self {
97 WasmValue::I32(v)
98 }
99}
100
101impl From<i64> for WasmValue {
102 fn from(v: i64) -> Self {
103 WasmValue::I64(v)
104 }
105}
106
107impl From<f32> for WasmValue {
108 fn from(v: f32) -> Self {
109 WasmValue::F32(v)
110 }
111}
112
113impl From<f64> for WasmValue {
114 fn from(v: f64) -> Self {
115 WasmValue::F64(v)
116 }
117}
118
119impl From<bool> for WasmValue {
120 fn from(v: bool) -> Self {
121 WasmValue::Bool(v)
122 }
123}
124
125impl From<String> for WasmValue {
126 fn from(v: String) -> Self {
127 WasmValue::String(v)
128 }
129}
130
131impl From<&str> for WasmValue {
132 fn from(v: &str) -> Self {
133 WasmValue::String(v.to_string())
134 }
135}
136
137impl From<Vec<u8>> for WasmValue {
138 fn from(v: Vec<u8>) -> Self {
139 WasmValue::Bytes(v)
140 }
141}
142
143#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
145pub enum WasmType {
146 I32,
147 I64,
148 F32,
149 F64,
150 V128,
151 FuncRef,
152 ExternRef,
153}
154
155impl fmt::Display for WasmType {
156 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157 match self {
158 WasmType::I32 => write!(f, "i32"),
159 WasmType::I64 => write!(f, "i64"),
160 WasmType::F32 => write!(f, "f32"),
161 WasmType::F64 => write!(f, "f64"),
162 WasmType::V128 => write!(f, "v128"),
163 WasmType::FuncRef => write!(f, "funcref"),
164 WasmType::ExternRef => write!(f, "externref"),
165 }
166 }
167}
168
169#[derive(Debug, Error)]
171pub enum WasmError {
172 #[error("Failed to compile WASM module: {0}")]
173 CompilationError(String),
174
175 #[error("Failed to instantiate WASM module: {0}")]
176 InstantiationError(String),
177
178 #[error("Failed to load WASM module: {0}")]
179 LoadError(String),
180
181 #[error("Export not found: {0}")]
182 ExportNotFound(String),
183
184 #[error("Import not found: {module}.{name}")]
185 ImportNotFound { module: String, name: String },
186
187 #[error("Type mismatch: expected {expected}, got {actual}")]
188 TypeMismatch { expected: String, actual: String },
189
190 #[error("Memory access out of bounds: offset={offset}, size={size}")]
191 MemoryOutOfBounds { offset: u32, size: u32 },
192
193 #[error("Memory allocation failed: requested {size} bytes")]
194 AllocationFailed { size: u32 },
195
196 #[error("Execution error: {0}")]
197 ExecutionError(String),
198
199 #[error("Timeout: execution exceeded {0}ms")]
200 Timeout(u64),
201
202 #[error("Resource limit exceeded: {0}")]
203 ResourceLimitExceeded(String),
204
205 #[error("Invalid plugin manifest: {0}")]
206 InvalidManifest(String),
207
208 #[error("Plugin not found: {0}")]
209 PluginNotFound(String),
210
211 #[error("Plugin already loaded: {0}")]
212 PluginAlreadyLoaded(String),
213
214 #[error("Host function error: {0}")]
215 HostFunctionError(String),
216
217 #[error("Serialization error: {0}")]
218 SerializationError(String),
219
220 #[error("IO error: {0}")]
221 IoError(#[from] std::io::Error),
222
223 #[error("Internal error: {0}")]
224 Internal(String),
225}
226
227pub type WasmResult<T> = Result<T, WasmError>;
229
230#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
232pub enum PluginCapability {
233 ReadConfig,
235 WriteConfig,
237 SendMessage,
239 ReceiveMessage,
241 CallTool,
243 Storage,
245 HttpClient,
247 FileSystem,
249 Timer,
251 Random,
253 Custom(String),
255}
256
257impl fmt::Display for PluginCapability {
258 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
259 match self {
260 PluginCapability::ReadConfig => write!(f, "read_config"),
261 PluginCapability::WriteConfig => write!(f, "write_config"),
262 PluginCapability::SendMessage => write!(f, "send_message"),
263 PluginCapability::ReceiveMessage => write!(f, "receive_message"),
264 PluginCapability::CallTool => write!(f, "call_tool"),
265 PluginCapability::Storage => write!(f, "storage"),
266 PluginCapability::HttpClient => write!(f, "http_client"),
267 PluginCapability::FileSystem => write!(f, "filesystem"),
268 PluginCapability::Timer => write!(f, "timer"),
269 PluginCapability::Random => write!(f, "random"),
270 PluginCapability::Custom(s) => write!(f, "custom:{}", s),
271 }
272 }
273}
274
275#[derive(Debug, Clone, Serialize, Deserialize)]
277pub struct PluginManifest {
278 pub name: String,
280 pub version: String,
282 pub description: Option<String>,
284 pub author: Option<String>,
286 pub license: Option<String>,
288 pub capabilities: Vec<PluginCapability>,
290 pub exports: Vec<PluginExport>,
292 pub min_runtime_version: Option<String>,
294 pub config_schema: Option<serde_json::Value>,
296 pub metadata: HashMap<String, String>,
298}
299
300impl Default for PluginManifest {
301 fn default() -> Self {
302 Self {
303 name: "unknown".to_string(),
304 version: "0.0.0".to_string(),
305 description: None,
306 author: None,
307 license: None,
308 capabilities: Vec::new(),
309 exports: Vec::new(),
310 min_runtime_version: None,
311 config_schema: None,
312 metadata: HashMap::new(),
313 }
314 }
315}
316
317impl PluginManifest {
318 pub fn new(name: &str, version: &str) -> Self {
319 Self {
320 name: name.to_string(),
321 version: version.to_string(),
322 ..Default::default()
323 }
324 }
325
326 pub fn with_description(mut self, description: &str) -> Self {
327 self.description = Some(description.to_string());
328 self
329 }
330
331 pub fn with_capability(mut self, capability: PluginCapability) -> Self {
332 if !self.capabilities.contains(&capability) {
333 self.capabilities.push(capability);
334 }
335 self
336 }
337
338 pub fn with_export(mut self, export: PluginExport) -> Self {
339 self.exports.push(export);
340 self
341 }
342
343 pub fn has_capability(&self, capability: &PluginCapability) -> bool {
344 self.capabilities.contains(capability)
345 }
346}
347
348#[derive(Debug, Clone, Serialize, Deserialize)]
350pub struct PluginExport {
351 pub name: String,
353 pub kind: ExportKind,
355 pub params: Vec<WasmType>,
357 pub returns: Vec<WasmType>,
359 pub description: Option<String>,
361}
362
363impl PluginExport {
364 pub fn function(name: &str, params: Vec<WasmType>, returns: Vec<WasmType>) -> Self {
365 Self {
366 name: name.to_string(),
367 kind: ExportKind::Function,
368 params,
369 returns,
370 description: None,
371 }
372 }
373
374 pub fn memory(name: &str) -> Self {
375 Self {
376 name: name.to_string(),
377 kind: ExportKind::Memory,
378 params: Vec::new(),
379 returns: Vec::new(),
380 description: None,
381 }
382 }
383
384 pub fn with_description(mut self, description: &str) -> Self {
385 self.description = Some(description.to_string());
386 self
387 }
388}
389
390#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
392pub enum ExportKind {
393 Function,
394 Memory,
395 Table,
396 Global,
397}
398
399#[derive(Debug, Clone, Serialize, Deserialize)]
401pub struct ResourceLimits {
402 pub max_memory_pages: u32,
404 pub max_table_elements: u32,
406 pub max_instances: u32,
408 pub max_execution_time_ms: u64,
410 pub max_fuel: Option<u64>,
412 pub max_call_depth: u32,
414}
415
416impl Default for ResourceLimits {
417 fn default() -> Self {
418 Self {
419 max_memory_pages: 256, max_table_elements: 10000,
421 max_instances: 10,
422 max_execution_time_ms: 30000, max_fuel: Some(100_000_000), max_call_depth: 1000,
425 }
426 }
427}
428
429impl ResourceLimits {
430 pub fn unlimited() -> Self {
431 Self {
432 max_memory_pages: u32::MAX,
433 max_table_elements: u32::MAX,
434 max_instances: u32::MAX,
435 max_execution_time_ms: u64::MAX,
436 max_fuel: None,
437 max_call_depth: u32::MAX,
438 }
439 }
440
441 pub fn restrictive() -> Self {
442 Self {
443 max_memory_pages: 16, max_table_elements: 1000,
445 max_instances: 1,
446 max_execution_time_ms: 5000, max_fuel: Some(10_000_000), max_call_depth: 100,
449 }
450 }
451
452 pub fn max_memory_bytes(&self) -> u64 {
453 self.max_memory_pages as u64 * 65536
454 }
455}
456
457#[derive(Debug, Clone, Serialize, Deserialize)]
459pub struct MemoryConfig {
460 pub initial_pages: u32,
462 pub maximum_pages: Option<u32>,
464 pub shared: bool,
466 pub growth_limit: Option<u32>,
468}
469
470impl Default for MemoryConfig {
471 fn default() -> Self {
472 Self {
473 initial_pages: 1, maximum_pages: Some(256), shared: false,
476 growth_limit: Some(16), }
478 }
479}
480
481#[derive(Debug, Clone, Serialize, Deserialize)]
483pub struct ExecutionConfig {
484 pub async_support: bool,
486 pub fuel_metering: bool,
488 pub epoch_interruption: bool,
490 pub epoch_tick_ms: u64,
492 pub debug_info: bool,
494 pub reference_types: bool,
496 pub simd: bool,
498 pub bulk_memory: bool,
500 pub multi_value: bool,
502 pub threads: bool,
504}
505
506impl Default for ExecutionConfig {
507 fn default() -> Self {
508 Self {
509 async_support: true,
510 fuel_metering: true,
511 epoch_interruption: true,
512 epoch_tick_ms: 10,
513 debug_info: false,
514 reference_types: true,
515 simd: true,
516 bulk_memory: true,
517 multi_value: true,
518 threads: false,
519 }
520 }
521}
522
523#[cfg(test)]
524mod tests {
525 use super::*;
526
527 #[test]
528 fn test_wasm_value_conversions() {
529 let v = WasmValue::from(42i32);
530 assert_eq!(v.as_i32(), Some(42));
531 assert_eq!(v.as_i64(), Some(42));
532
533 let v = WasmValue::from("hello");
534 assert_eq!(v.as_string(), Some("hello"));
535
536 let v = WasmValue::from(true);
537 assert_eq!(v.as_bool(), Some(true));
538 }
539
540 #[test]
541 fn test_plugin_manifest() {
542 let manifest = PluginManifest::new("test-plugin", "1.0.0")
543 .with_description("A test plugin")
544 .with_capability(PluginCapability::ReadConfig)
545 .with_capability(PluginCapability::SendMessage);
546
547 assert_eq!(manifest.name, "test-plugin");
548 assert_eq!(manifest.version, "1.0.0");
549 assert!(manifest.has_capability(&PluginCapability::ReadConfig));
550 assert!(!manifest.has_capability(&PluginCapability::Storage));
551 }
552
553 #[test]
554 fn test_resource_limits() {
555 let limits = ResourceLimits::default();
556 assert_eq!(limits.max_memory_bytes(), 16 * 1024 * 1024); let restrictive = ResourceLimits::restrictive();
559 assert_eq!(restrictive.max_memory_bytes(), 1024 * 1024); }
561}