1use crate::effects::Effect;
2use crate::ids::{FuncId, ModuleId, TypeId};
3use crate::node::Node;
4use crate::types::Type;
5use serde::{Deserialize, Serialize};
6
7#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
9pub struct Module {
10 pub format_version: String,
12 pub module: ModuleInner,
14}
15
16#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
18pub struct ModuleInner {
19 pub id: ModuleId,
21 pub name: String,
23 pub metadata: ModuleMetadata,
25 pub imports: Vec<Import>,
27 pub exports: Vec<Export>,
29 pub types: Vec<TypeDef>,
31 #[serde(default)]
33 pub traits: Vec<serde_json::Value>,
34 #[serde(default)]
36 pub impls: Vec<serde_json::Value>,
37 #[serde(default)]
39 pub constants: Vec<serde_json::Value>,
40 pub functions: Vec<FuncDef>,
42}
43
44#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
46pub struct ModuleMetadata {
47 pub version: String,
49 pub description: String,
51 pub author: String,
53 pub created_at: String,
55}
56
57#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
59pub struct Import {
60 pub module: String,
62 pub items: Vec<String>,
64}
65
66#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
68pub struct Export {
69 pub kind: String,
71 pub name: String,
73}
74
75#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
78pub struct TypeDef {
79 pub id: TypeId,
81 #[serde(flatten)]
83 pub data: serde_json::Value,
84}
85
86#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
88pub struct FuncDef {
89 pub id: FuncId,
91 pub name: String,
93 #[serde(default)]
95 pub params: Vec<ParamDef>,
96 pub returns: Type,
98 #[serde(default)]
100 pub effects: Vec<Effect>,
101 pub body: Node,
103}
104
105#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
107pub struct ParamDef {
108 pub name: String,
110 #[serde(rename = "type")]
112 pub param_type: Type,
113 #[serde(default)]
115 pub index: u32,
116}
117
118impl Module {
119 pub fn id(&self) -> &ModuleId {
121 &self.module.id
122 }
123
124 pub fn name(&self) -> &str {
126 &self.module.name
127 }
128
129 pub fn functions(&self) -> &[FuncDef] {
131 &self.module.functions
132 }
133
134 pub fn find_function(&self, name: &str) -> Option<&FuncDef> {
136 self.module.functions.iter().find(|f| f.name == name)
137 }
138
139 pub fn find_function_by_id(&self, id: &FuncId) -> Option<&FuncDef> {
141 self.module.functions.iter().find(|f| &f.id == id)
142 }
143}
144
145impl FuncDef {
146 pub fn has_effect(&self, effect: &Effect) -> bool {
148 self.effects.contains(effect)
149 }
150
151 pub fn is_pure(&self) -> bool {
153 self.effects.is_empty() || (self.effects.len() == 1 && self.effects[0] == Effect::Pure)
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 #[test]
162 fn test_hello_world_module_deserialize() {
163 let json = r#"{
164 "format_version": "0.1.0",
165 "module": {
166 "id": "mod_main",
167 "name": "main",
168 "metadata": {
169 "version": "1.0.0",
170 "description": "Hello world program",
171 "author": "agent-001",
172 "created_at": "2026-04-06T12:00:00Z"
173 },
174 "imports": [
175 { "module": "std::io", "items": ["println"] }
176 ],
177 "exports": [
178 { "kind": "Function", "name": "main" }
179 ],
180 "types": [],
181 "traits": [],
182 "impls": [],
183 "constants": [],
184 "functions": [
185 {
186 "id": "f_main",
187 "name": "main",
188 "params": [],
189 "returns": "Unit",
190 "effects": ["IO"],
191 "body": {
192 "id": "n_100",
193 "kind": "Call",
194 "type": "Unit",
195 "target": "std::io::println",
196 "args": [
197 {
198 "id": "n_101",
199 "kind": "Literal",
200 "type": "String",
201 "value": "hello world"
202 }
203 ]
204 }
205 }
206 ]
207 }
208 }"#;
209
210 let module: Module = serde_json::from_str(json).unwrap();
211 assert_eq!(module.format_version, "0.1.0");
212 assert_eq!(module.module.id, ModuleId::new("mod_main"));
213 assert_eq!(module.module.name, "main");
214 assert_eq!(module.module.metadata.description, "Hello world program");
215 assert_eq!(module.module.imports.len(), 1);
216 assert_eq!(module.module.exports.len(), 1);
217 assert_eq!(module.module.functions.len(), 1);
218
219 let func = &module.module.functions[0];
220 assert_eq!(func.name, "main");
221 assert_eq!(func.returns, Type::Unit);
222 assert_eq!(func.effects, vec![Effect::IO]);
223
224 match &func.body {
225 crate::node::Node::Call { target, args, .. } => {
226 assert_eq!(target, "std::io::println");
227 assert_eq!(args.len(), 1);
228 }
229 other => panic!("Expected Call node, got: {other:?}"),
230 }
231 }
232
233 #[test]
234 fn test_module_roundtrip() {
235 let json = r#"{
236 "format_version": "0.1.0",
237 "module": {
238 "id": "mod_main",
239 "name": "main",
240 "metadata": {
241 "version": "1.0.0",
242 "description": "Test",
243 "author": "test",
244 "created_at": "2026-01-01T00:00:00Z"
245 },
246 "imports": [],
247 "exports": [],
248 "types": [],
249 "traits": [],
250 "impls": [],
251 "constants": [],
252 "functions": [
253 {
254 "id": "f_main",
255 "name": "main",
256 "params": [],
257 "returns": "Unit",
258 "effects": ["Pure"],
259 "body": {
260 "id": "n_1",
261 "kind": "Literal",
262 "type": "Unit",
263 "value": null
264 }
265 }
266 ]
267 }
268 }"#;
269
270 let module: Module = serde_json::from_str(json).unwrap();
271 let serialized = serde_json::to_string_pretty(&module).unwrap();
272 let reparsed: Module = serde_json::from_str(&serialized).unwrap();
273 assert_eq!(module, reparsed);
274 }
275}