amalgam_core/
ir.rs

1//! Intermediate representation for cross-language transformations
2
3use crate::types::Type;
4use serde::{Deserialize, Serialize};
5use std::collections::BTreeMap;
6
7/// Intermediate representation of a schema/module
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct IR {
10    pub modules: Vec<Module>,
11}
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct Module {
15    pub name: String,
16    pub imports: Vec<Import>,
17    pub types: Vec<TypeDefinition>,
18    pub constants: Vec<Constant>,
19    pub metadata: Metadata,
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct Import {
24    pub path: String,
25    pub alias: Option<String>,
26    pub items: Vec<String>, // Specific items to import
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct TypeDefinition {
31    pub name: String,
32    pub ty: Type,
33    pub documentation: Option<String>,
34    pub annotations: BTreeMap<String, serde_json::Value>,
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct Constant {
39    pub name: String,
40    pub ty: Type,
41    pub value: serde_json::Value,
42    pub documentation: Option<String>,
43}
44
45#[derive(Debug, Clone, Default, Serialize, Deserialize)]
46pub struct Metadata {
47    pub source_language: Option<String>,
48    pub source_file: Option<String>,
49    pub version: Option<String>,
50    pub generated_at: Option<String>,
51    pub custom: BTreeMap<String, serde_json::Value>,
52}
53
54impl IR {
55    pub fn new() -> Self {
56        Self {
57            modules: Vec::new(),
58        }
59    }
60
61    pub fn add_module(&mut self, module: Module) {
62        self.modules.push(module);
63    }
64
65    pub fn find_type(&self, name: &str) -> Option<&TypeDefinition> {
66        self.modules
67            .iter()
68            .flat_map(|m| &m.types)
69            .find(|t| t.name == name)
70    }
71
72    pub fn merge(mut self, other: IR) -> Self {
73        self.modules.extend(other.modules);
74        self
75    }
76}
77
78impl Default for IR {
79    fn default() -> Self {
80        Self::new()
81    }
82}
83
84/// Builder pattern for constructing IR
85pub struct IRBuilder {
86    ir: IR,
87    current_module: Option<Module>,
88}
89
90impl IRBuilder {
91    pub fn new() -> Self {
92        Self {
93            ir: IR::new(),
94            current_module: None,
95        }
96    }
97
98    pub fn module(mut self, name: impl Into<String>) -> Self {
99        if let Some(module) = self.current_module.take() {
100            self.ir.add_module(module);
101        }
102        self.current_module = Some(Module {
103            name: name.into(),
104            imports: Vec::new(),
105            types: Vec::new(),
106            constants: Vec::new(),
107            metadata: Metadata::default(),
108        });
109        self
110    }
111
112    pub fn add_type(mut self, name: impl Into<String>, ty: Type) -> Self {
113        if let Some(ref mut module) = self.current_module {
114            module.types.push(TypeDefinition {
115                name: name.into(),
116                ty,
117                documentation: None,
118                annotations: BTreeMap::new(),
119            });
120        }
121        self
122    }
123
124    pub fn add_import(mut self, path: impl Into<String>) -> Self {
125        if let Some(ref mut module) = self.current_module {
126            module.imports.push(Import {
127                path: path.into(),
128                alias: None,
129                items: Vec::new(),
130            });
131        }
132        self
133    }
134
135    pub fn build(mut self) -> IR {
136        if let Some(module) = self.current_module.take() {
137            self.ir.add_module(module);
138        }
139        self.ir
140    }
141}
142
143impl Default for IRBuilder {
144    fn default() -> Self {
145        Self::new()
146    }
147}
148
149#[cfg(test)]
150mod tests {
151    use super::*;
152
153    #[test]
154    fn test_ir_builder() {
155        let ir = IRBuilder::new()
156            .module("test")
157            .add_type("MyType", Type::String)
158            .add_type("MyNumber", Type::Number)
159            .build();
160
161        assert_eq!(ir.modules.len(), 1);
162        assert_eq!(ir.modules[0].name, "test");
163        assert_eq!(ir.modules[0].types.len(), 2);
164
165        let my_type = ir.find_type("MyType");
166        assert!(my_type.is_some());
167        assert_eq!(my_type.unwrap().ty, Type::String);
168    }
169}