Skip to main content

airl_ir/
module.rs

1use crate::effects::Effect;
2use crate::ids::{FuncId, ModuleId, TypeId};
3use crate::node::Node;
4use crate::types::Type;
5use serde::{Deserialize, Serialize};
6
7/// Top-level module container matching the JSON IR format.
8#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
9pub struct Module {
10    /// IR format version (semver-style, e.g. `"0.1.0"`).
11    pub format_version: String,
12    /// The inner module definition.
13    pub module: ModuleInner,
14}
15
16/// The inner module definition containing all declarations.
17#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
18pub struct ModuleInner {
19    /// Module ID (unique identifier, typed).
20    pub id: ModuleId,
21    /// Human-readable module name.
22    pub name: String,
23    /// Descriptive metadata (version, author, description).
24    pub metadata: ModuleMetadata,
25    /// List of `use module::item` imports.
26    pub imports: Vec<Import>,
27    /// List of exports this module publishes.
28    pub exports: Vec<Export>,
29    /// Type definitions in this module.
30    pub types: Vec<TypeDef>,
31    /// Trait definitions (reserved for future use).
32    #[serde(default)]
33    pub traits: Vec<serde_json::Value>,
34    /// Trait implementations (reserved for future use).
35    #[serde(default)]
36    pub impls: Vec<serde_json::Value>,
37    /// Compile-time constants (reserved for future use).
38    #[serde(default)]
39    pub constants: Vec<serde_json::Value>,
40    /// Function definitions in this module.
41    pub functions: Vec<FuncDef>,
42}
43
44/// Module metadata.
45#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
46pub struct ModuleMetadata {
47    /// Module version (semver-style).
48    pub version: String,
49    /// Human-readable description.
50    pub description: String,
51    /// Author name or agent ID.
52    pub author: String,
53    /// ISO-8601 creation timestamp.
54    pub created_at: String,
55}
56
57/// An import declaration: `use module::item`.
58#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
59pub struct Import {
60    /// The module path being imported from (e.g. `"std::io"`, `"mathlib"`).
61    pub module: String,
62    /// Names of items to import from the module.
63    pub items: Vec<String>,
64}
65
66/// An export declaration: makes an item visible to other modules.
67#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
68pub struct Export {
69    /// Kind of exported item (`"Function"`, `"Type"`, `"Constant"`).
70    pub kind: String,
71    /// Name of the item being exported.
72    pub name: String,
73}
74
75/// A type definition. Currently uses `serde_json::Value` for the body
76/// but has a typed `id` field.
77#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
78pub struct TypeDef {
79    /// Type ID (unique identifier).
80    pub id: TypeId,
81    /// Type-specific data (struct fields, enum variants, etc.) as raw JSON.
82    #[serde(flatten)]
83    pub data: serde_json::Value,
84}
85
86/// A function definition.
87#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
88pub struct FuncDef {
89    /// Function ID (unique within the module).
90    pub id: FuncId,
91    /// Function name (used for calls and exports).
92    pub name: String,
93    /// Parameter list.
94    #[serde(default)]
95    pub params: Vec<ParamDef>,
96    /// Return type.
97    pub returns: Type,
98    /// Declared effects this function may have.
99    #[serde(default)]
100    pub effects: Vec<Effect>,
101    /// The function body (an IR node tree).
102    pub body: Node,
103}
104
105/// A function parameter definition.
106#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
107pub struct ParamDef {
108    /// Parameter name (used as a local variable within the function body).
109    pub name: String,
110    /// Parameter type.
111    #[serde(rename = "type")]
112    pub param_type: Type,
113    /// Position in the parameter list (0-based).
114    #[serde(default)]
115    pub index: u32,
116}
117
118impl Module {
119    /// Get the module ID.
120    pub fn id(&self) -> &ModuleId {
121        &self.module.id
122    }
123
124    /// Get the module name.
125    pub fn name(&self) -> &str {
126        &self.module.name
127    }
128
129    /// Get all function definitions.
130    pub fn functions(&self) -> &[FuncDef] {
131        &self.module.functions
132    }
133
134    /// Find a function by name.
135    pub fn find_function(&self, name: &str) -> Option<&FuncDef> {
136        self.module.functions.iter().find(|f| f.name == name)
137    }
138
139    /// Find a function by ID.
140    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    /// Check if this function has a specific effect.
147    pub fn has_effect(&self, effect: &Effect) -> bool {
148        self.effects.contains(effect)
149    }
150
151    /// Check if this function is pure (no side effects).
152    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}