Skip to main content

max / audiofiles

3.1 KB · 103 lines History Blame Raw
1 //! Rhai engine setup: sandboxed engine with registered types and host API.
2
3 use rhai::Engine;
4
5 use crate::host_api;
6 use crate::types::{self, RhaiExportContext, RhaiSampleInfo};
7
8 /// Create a sandboxed Rhai engine with all custom types and host functions registered.
9 pub fn create_engine() -> Engine {
10 let mut engine = Engine::new();
11
12 // Sandbox limits
13 engine.set_max_operations(100_000);
14 engine.set_max_call_levels(32);
15 engine.set_max_string_size(10_000);
16 engine.set_max_array_size(1_000);
17 engine.set_max_map_size(100);
18 engine.set_max_expr_depths(64, 32);
19
20 // Suppress print/debug output from scripts
21 engine.on_print(|_| {});
22 engine.on_debug(|_, _, _| {});
23
24 // Disable dynamic eval and module imports (principle of least privilege)
25 engine.disable_symbol("eval");
26 engine.set_max_modules(0);
27
28 // Register custom types
29 engine
30 .register_type_with_name::<RhaiSampleInfo>("SampleInfo")
31 .register_type_with_name::<RhaiExportContext>("ExportContext");
32
33 // Register getter modules
34 engine.register_global_module(rhai::exported_module!(types::sample_info_module).into());
35 engine.register_global_module(rhai::exported_module!(types::export_context_module).into());
36
37 // Register host API functions
38 host_api::register(&mut engine);
39
40 engine
41 }
42
43 #[cfg(test)]
44 mod tests {
45 use super::*;
46
47 #[test]
48 fn engine_creates_without_panic() {
49 let _engine = create_engine();
50 }
51
52 #[test]
53 fn engine_enforces_operation_limit() {
54 let engine = create_engine();
55 let result = engine.eval::<()>("let x = 0; loop { x += 1; }");
56 assert!(result.is_err(), "infinite loop should be stopped by operation limit");
57 }
58
59 #[test]
60 fn engine_sample_info_getters_work() {
61 let engine = create_engine();
62 let mut scope = rhai::Scope::new();
63 let info = RhaiSampleInfo {
64 hash: "abc123".to_string(),
65 name: "kick.wav".to_string(),
66 extension: "wav".to_string(),
67 sample_rate: 44100,
68 bit_depth: 16,
69 channels: 1,
70 duration: 0.5,
71 file_size: 44100,
72 };
73 scope.push("info", info);
74
75 let result: String = engine.eval_with_scope(&mut scope, "info.name").unwrap();
76 assert_eq!(result, "kick.wav");
77
78 let rate: i64 = engine.eval_with_scope(&mut scope, "info.sample_rate").unwrap();
79 assert_eq!(rate, 44100);
80 }
81
82 #[test]
83 fn engine_export_context_getters_work() {
84 let engine = create_engine();
85 let mut scope = rhai::Scope::new();
86 let ctx = RhaiExportContext {
87 device_name: "SP-404 MKII".to_string(),
88 destination: "/tmp/export".to_string(),
89 filename: "kick".to_string(),
90 extension: "wav".to_string(),
91 index: 0,
92 total: 10,
93 };
94 scope.push("ctx", ctx);
95
96 let result: String = engine.eval_with_scope(&mut scope, "ctx.device_name").unwrap();
97 assert_eq!(result, "SP-404 MKII");
98
99 let total: i64 = engine.eval_with_scope(&mut scope, "ctx.total").unwrap();
100 assert_eq!(total, 10);
101 }
102 }
103