1use crate::index::Registry;
2use std::collections::HashSet;
3
4pub struct Monomorphizer<'a> {
5 registry: &'a mut Registry,
6 _processed_generics: HashSet<String>,
7}
8
9impl<'a> Monomorphizer<'a> {
10 pub fn new(registry: &'a mut Registry) -> Self {
11 Self {
12 registry,
13 _processed_generics: HashSet::new(),
14 }
15 }
16
17 pub fn process(&mut self, content: &str) -> String {
20 self.resolve_generics_in_text(content)
21 }
22
23 fn resolve_generics_in_text(&mut self, text: &str) -> String {
24 let mut result = String::new();
25 let chars: Vec<char> = text.chars().collect();
26 let mut i = 0;
27
28 while i < chars.len() {
29 if chars[i] == '$' && i + 1 < chars.len() && chars[i + 1].is_alphabetic() {
30 let start = i;
32 i += 1;
33 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
34 i += 1;
35 }
36 let name: String = chars[start + 1..i].iter().collect();
37
38 if i < chars.len() && chars[i] == '<' {
39 i += 1; let arg_start = i;
42 let mut depth = 1;
43 while i < chars.len() && depth > 0 {
44 if chars[i] == '<' {
45 depth += 1;
46 } else if chars[i] == '>' {
47 depth -= 1;
48 }
49 i += 1;
50 }
51 let args_str: String = chars[arg_start..i - 1].iter().collect();
54
55 let concrete_name = self.monomorphize(&name, &args_str);
57
58 result.push('$');
60 result.push_str(&concrete_name);
61 } else {
62 result.push_str(&text[start..i]);
64 }
65 } else {
66 result.push(chars[i]);
67 i += 1;
68 }
69 }
70 result
71 }
72
73 pub fn monomorphize(&mut self, name: &str, args_str: &str) -> String {
76 let args = self.split_args(args_str);
78
79 let resolved_args: Vec<String> = args
81 .into_iter()
82 .map(|arg| {
83 if arg.contains('<') {
84 let processed = self.resolve_generics_in_text(&arg);
85 processed.trim_start_matches('$').to_string()
86 } else {
87 arg.trim_start_matches('$').to_string()
88 }
89 })
90 .collect();
91
92 let suffix = if resolved_args.is_empty() {
94 "Generic".to_string()
95 } else {
96 resolved_args.join("_")
97 };
98 let concrete_name = format!("{}_{}", name, suffix);
99
100 if self.registry.concrete_schemas.contains_key(&concrete_name) {
101 return concrete_name;
102 }
103
104 if let Some(blueprint) = self.registry.blueprints.get(name).cloned() {
106 let mut content = blueprint.body.clone();
107
108 if resolved_args.len() != blueprint.params.len() {
110 log::error!(
111 "Blueprint {} expects {} args, got {}. Using raw args.",
112 name,
113 blueprint.params.len(),
114 resolved_args.len()
115 );
116 }
117
118 for (idx, param) in blueprint.params.iter().enumerate() {
120 if let Some(arg) = resolved_args.get(idx) {
121 let target = format!("${}", param);
124 let replacement = format!("${}", arg);
125 content = content.replace(&target, &replacement);
126 }
127 }
128
129 self.registry
130 .concrete_schemas
131 .insert(concrete_name.clone(), content);
132 } else {
133 log::warn!("Blueprint {} not found", name);
134 }
135
136 concrete_name
137 }
138
139 fn split_args(&self, args_str: &str) -> Vec<String> {
140 let mut args = Vec::new();
141 let mut start = 0;
142 let mut depth = 0;
143 let chars = args_str.char_indices().peekable();
144
145 if args_str.trim().is_empty() {
146 return Vec::new();
147 }
148 for (i, c) in chars {
149 match c {
150 '<' => depth += 1,
151 '>' => depth -= 1,
152 ',' if depth == 0 => {
153 args.push(args_str[start..i].trim().to_string());
154 start = i + 1;
156 }
157 _ => {}
158 }
159 }
160 if start < args_str.len() {
161 args.push(args_str[start..].trim().to_string());
162 }
163 args
164 }
165}
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170 #[test]
171 fn test_monomorphize_named() {
172 let mut registry = Registry::new();
173 registry.insert_blueprint(
174 "Page".to_string(),
175 vec!["T".to_string()],
176 "data: $ref: $T".to_string(),
177 );
178
179 let mut mono = Monomorphizer::new(&mut registry);
180 let result = mono.process("scheme: $ref: $Page<User>");
181
182 assert_eq!(result, "scheme: $ref: $Page_User");
184
185 let concrete = registry.concrete_schemas.get("Page_User").unwrap();
187 assert_eq!(concrete, "data: $ref: $User");
188 }
189
190 #[test]
191 fn test_nested_generics() {
192 let mut registry = Registry::new();
193 registry.insert_blueprint(
194 "Wrapper".to_string(),
195 vec!["T".to_string()],
196 "wrap: $T".to_string(),
197 );
198 registry.insert_blueprint(
199 "Inner".to_string(),
200 vec!["U".to_string()],
201 "in: $U".to_string(),
202 );
203
204 let mut mono = Monomorphizer::new(&mut registry);
205 let result = mono.process("$Wrapper<$Inner<Item>>");
206
207 assert_eq!(result, "$Wrapper_Inner_Item");
208
209 assert!(registry.concrete_schemas.contains_key("Inner_Item"));
211 let inner = registry.concrete_schemas.get("Inner_Item").unwrap();
212 assert_eq!(inner, "in: $Item");
213
214 assert!(registry.concrete_schemas.contains_key("Wrapper_Inner_Item"));
216 let wrapper = registry.concrete_schemas.get("Wrapper_Inner_Item").unwrap();
217 assert_eq!(wrapper, "wrap: $Inner_Item");
219 }
220}