solverforge_core/wasm/
host_functions.rs

1use indexmap::IndexMap;
2use serde::{Deserialize, Serialize};
3
4/// WASM value types
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
6pub enum WasmType {
7    /// 32-bit integer
8    I32,
9    /// 64-bit integer
10    I64,
11    /// 32-bit float
12    F32,
13    /// 64-bit float
14    F64,
15    /// Pointer (i32 in WASM)
16    Ptr,
17    /// No return value
18    Void,
19}
20
21/// Host function definition
22#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct HostFunctionDef {
24    /// Function name (as it appears in imports)
25    pub name: String,
26    /// Parameter types
27    pub params: Vec<WasmType>,
28    /// Return type
29    pub return_type: WasmType,
30}
31
32impl HostFunctionDef {
33    pub fn new(name: impl Into<String>, params: Vec<WasmType>, return_type: WasmType) -> Self {
34        Self {
35            name: name.into(),
36            params,
37            return_type,
38        }
39    }
40}
41
42/// Registry of host functions available for import into WASM modules
43/// Uses IndexMap to preserve insertion order for deterministic function indices
44#[derive(Debug, Clone, Default)]
45pub struct HostFunctionRegistry {
46    functions: IndexMap<String, HostFunctionDef>,
47}
48
49impl HostFunctionRegistry {
50    /// Create a new registry with standard SolverForge host functions
51    pub fn with_standard_functions() -> Self {
52        let mut registry = Self::default();
53
54        // String operations
55        registry.register(HostFunctionDef::new(
56            "hstringEquals",
57            vec![WasmType::Ptr, WasmType::Ptr],
58            WasmType::I32,
59        ));
60
61        // List operations
62        registry.register(HostFunctionDef::new(
63            "hlistContainsString",
64            vec![WasmType::Ptr, WasmType::Ptr],
65            WasmType::I32,
66        ));
67        registry.register(HostFunctionDef::new("hnewList", vec![], WasmType::Ptr));
68
69        registry.register(HostFunctionDef::new(
70            "hgetItem",
71            vec![WasmType::Ptr, WasmType::I32],
72            WasmType::Ptr,
73        ));
74
75        registry.register(HostFunctionDef::new(
76            "hsize",
77            vec![WasmType::Ptr],
78            WasmType::I32,
79        ));
80
81        registry.register(HostFunctionDef::new(
82            "happend",
83            vec![WasmType::Ptr, WasmType::Ptr],
84            WasmType::Void,
85        ));
86
87        registry.register(HostFunctionDef::new(
88            "hsetItem",
89            vec![WasmType::Ptr, WasmType::I32, WasmType::Ptr],
90            WasmType::Void,
91        ));
92
93        registry.register(HostFunctionDef::new(
94            "hinsert",
95            vec![WasmType::Ptr, WasmType::I32, WasmType::Ptr],
96            WasmType::Void,
97        ));
98
99        registry.register(HostFunctionDef::new(
100            "hremove",
101            vec![WasmType::Ptr, WasmType::I32],
102            WasmType::Void,
103        ));
104
105        // Schedule operations
106        registry.register(HostFunctionDef::new(
107            "hparseSchedule",
108            vec![WasmType::I32, WasmType::Ptr], // (length: i32, json: ptr) -> ptr
109            WasmType::Ptr,
110        ));
111
112        registry.register(HostFunctionDef::new(
113            "hscheduleString",
114            vec![WasmType::Ptr],
115            WasmType::Ptr,
116        ));
117
118        // Math operations
119        registry.register(HostFunctionDef::new(
120            "hround",
121            vec![WasmType::F32],
122            WasmType::I32,
123        ));
124
125        registry
126    }
127
128    /// Create an empty registry
129    pub fn new() -> Self {
130        Self::default()
131    }
132
133    /// Register a host function
134    pub fn register(&mut self, def: HostFunctionDef) {
135        self.functions.insert(def.name.clone(), def);
136    }
137
138    /// Look up a host function by name
139    pub fn lookup(&self, name: &str) -> Option<&HostFunctionDef> {
140        self.functions.get(name)
141    }
142
143    /// Get all registered function names
144    pub fn function_names(&self) -> Vec<&str> {
145        self.functions.keys().map(|s| s.as_str()).collect()
146    }
147
148    /// Get the number of registered functions
149    pub fn len(&self) -> usize {
150        self.functions.len()
151    }
152
153    /// Check if the registry is empty
154    pub fn is_empty(&self) -> bool {
155        self.functions.is_empty()
156    }
157}
158
159#[cfg(test)]
160mod tests {
161    use super::*;
162
163    #[test]
164    fn test_empty_registry() {
165        let registry = HostFunctionRegistry::new();
166        assert_eq!(registry.len(), 0);
167        assert!(registry.is_empty());
168    }
169
170    #[test]
171    fn test_register_function() {
172        let mut registry = HostFunctionRegistry::new();
173        let func = HostFunctionDef::new(
174            "test_func",
175            vec![WasmType::I32, WasmType::Ptr],
176            WasmType::I32,
177        );
178
179        registry.register(func.clone());
180        assert_eq!(registry.len(), 1);
181
182        let looked_up = registry.lookup("test_func");
183        assert!(looked_up.is_some());
184        assert_eq!(looked_up.unwrap(), &func);
185    }
186
187    #[test]
188    fn test_lookup_nonexistent() {
189        let registry = HostFunctionRegistry::new();
190        assert!(registry.lookup("nonexistent").is_none());
191    }
192
193    #[test]
194    fn test_standard_functions() {
195        let registry = HostFunctionRegistry::with_standard_functions();
196
197        // Should have the standard set of functions
198        assert!(registry.len() > 0);
199
200        // Check for key functions
201        assert!(registry.lookup("hstringEquals").is_some());
202        assert!(registry.lookup("hnewList").is_some());
203        assert!(registry.lookup("hgetItem").is_some());
204        assert!(registry.lookup("hsize").is_some());
205        assert!(registry.lookup("happend").is_some());
206        assert!(registry.lookup("hparseSchedule").is_some());
207        assert!(registry.lookup("hscheduleString").is_some());
208        assert!(registry.lookup("hround").is_some());
209    }
210
211    #[test]
212    fn test_hstring_equals_signature() {
213        let registry = HostFunctionRegistry::with_standard_functions();
214        let func = registry.lookup("hstringEquals").unwrap();
215
216        assert_eq!(func.name, "hstringEquals");
217        assert_eq!(func.params, vec![WasmType::Ptr, WasmType::Ptr]);
218        assert_eq!(func.return_type, WasmType::I32);
219    }
220
221    #[test]
222    fn test_function_names() {
223        let mut registry = HostFunctionRegistry::new();
224        registry.register(HostFunctionDef::new("func1", vec![], WasmType::Void));
225        registry.register(HostFunctionDef::new("func2", vec![], WasmType::Void));
226
227        let names = registry.function_names();
228        assert_eq!(names.len(), 2);
229        assert!(names.contains(&"func1"));
230        assert!(names.contains(&"func2"));
231    }
232
233    #[test]
234    fn test_wasm_type_serialization() {
235        let wasm_type = WasmType::I32;
236        let json = serde_json::to_string(&wasm_type).unwrap();
237        let deserialized: WasmType = serde_json::from_str(&json).unwrap();
238        assert_eq!(wasm_type, deserialized);
239    }
240}