1use std::collections::HashMap;
2use std::path::PathBuf;
3use syn::{Field, Fields, Item, ItemStruct, Path, PathSegment, Type, TypePath, TypeReference};
4
5#[derive(Debug, Clone)]
7pub struct GlobalTypeRegistry {
8 pub types: HashMap<String, TypeDefinition>,
10 pub module_exports: HashMap<Vec<String>, Vec<String>>,
12 pub type_aliases: HashMap<String, String>,
14 pub imports: HashMap<PathBuf, ImportScope>,
16}
17
18#[derive(Debug, Clone)]
20pub struct TypeDefinition {
21 pub name: String,
22 pub kind: TypeKind,
23 pub fields: Option<FieldRegistry>,
24 pub methods: Vec<MethodSignature>,
25 pub generic_params: Vec<String>,
26 pub module_path: Vec<String>,
27}
28
29#[derive(Debug, Clone)]
31pub struct FieldRegistry {
32 pub named_fields: HashMap<String, ResolvedFieldType>,
34 pub tuple_fields: Vec<ResolvedFieldType>,
36}
37
38#[derive(Debug, Clone)]
40pub struct ResolvedFieldType {
41 pub type_name: String,
42 pub is_reference: bool,
43 pub is_mutable: bool,
44 pub generic_args: Vec<String>,
45}
46
47#[derive(Debug, Clone)]
49pub struct MethodSignature {
50 pub name: String,
51 pub self_param: Option<SelfParam>,
52 pub return_type: Option<String>,
53 pub param_types: Vec<String>,
54}
55
56#[derive(Debug, Clone)]
58pub struct SelfParam {
59 pub is_reference: bool,
60 pub is_mutable: bool,
61}
62
63#[derive(Debug, Clone, PartialEq)]
65pub enum TypeKind {
66 Struct,
67 Enum,
68 Trait,
69 TypeAlias,
70 TupleStruct,
71 UnitStruct,
72}
73
74#[derive(Debug, Clone)]
76pub struct ImportScope {
77 pub imports: HashMap<String, String>, pub wildcard_imports: Vec<Vec<String>>,
81}
82
83impl Default for GlobalTypeRegistry {
84 fn default() -> Self {
85 Self::new()
86 }
87}
88
89impl GlobalTypeRegistry {
90 pub fn new() -> Self {
91 Self {
92 types: HashMap::new(),
93 module_exports: HashMap::new(),
94 type_aliases: HashMap::new(),
95 imports: HashMap::new(),
96 }
97 }
98
99 pub fn register_struct(&mut self, module_path: Vec<String>, item: &ItemStruct) {
101 let name = item.ident.to_string();
102 let full_name = if module_path.is_empty() {
103 name.clone()
104 } else {
105 format!("{}::{}", module_path.join("::"), name)
106 };
107
108 let fields = self.extract_fields(&item.fields);
109 let generic_params = item
110 .generics
111 .params
112 .iter()
113 .filter_map(|param| match param {
114 syn::GenericParam::Type(type_param) => Some(type_param.ident.to_string()),
115 _ => None,
116 })
117 .collect();
118
119 let kind = match &item.fields {
120 Fields::Named(_) => TypeKind::Struct,
121 Fields::Unnamed(_) => TypeKind::TupleStruct,
122 Fields::Unit => TypeKind::UnitStruct,
123 };
124
125 let type_def = TypeDefinition {
126 name: full_name.clone(),
127 kind,
128 fields: Some(fields),
129 methods: Vec::new(),
130 generic_params,
131 module_path: module_path.clone(),
132 };
133
134 self.types.insert(full_name, type_def);
135
136 self.module_exports
138 .entry(module_path)
139 .or_default()
140 .push(name);
141 }
142
143 fn extract_fields(&self, fields: &Fields) -> FieldRegistry {
145 match fields {
146 Fields::Named(named_fields) => {
147 let mut named = HashMap::new();
148 for field in &named_fields.named {
149 if let Some(ident) = &field.ident {
150 let field_type = self.extract_field_type(field);
151 named.insert(ident.to_string(), field_type);
152 }
153 }
154 FieldRegistry {
155 named_fields: named,
156 tuple_fields: Vec::new(),
157 }
158 }
159 Fields::Unnamed(unnamed_fields) => {
160 let tuple_fields = unnamed_fields
161 .unnamed
162 .iter()
163 .map(|field| self.extract_field_type(field))
164 .collect();
165 FieldRegistry {
166 named_fields: HashMap::new(),
167 tuple_fields,
168 }
169 }
170 Fields::Unit => FieldRegistry {
171 named_fields: HashMap::new(),
172 tuple_fields: Vec::new(),
173 },
174 }
175 }
176
177 #[allow(dead_code)]
179 fn extract_type_name_from_path(path: &syn::Path) -> String {
180 path.segments
181 .iter()
182 .map(|seg| seg.ident.to_string())
183 .collect::<Vec<_>>()
184 .join("::")
185 }
186
187 fn extract_field_type(&self, field: &Field) -> ResolvedFieldType {
189 match &field.ty {
190 Type::Path(type_path) => Self::extract_path_type(type_path),
191 Type::Reference(type_ref) => Self::extract_reference_type(type_ref),
192 _ => Self::unknown_field_type(),
193 }
194 }
195
196 fn extract_path_type(type_path: &TypePath) -> ResolvedFieldType {
198 let type_name = Self::build_type_name(&type_path.path);
199 let generic_args = Self::extract_generic_args(&type_path.path);
200
201 ResolvedFieldType {
202 type_name,
203 is_reference: false,
204 is_mutable: false,
205 generic_args,
206 }
207 }
208
209 fn extract_reference_type(type_ref: &TypeReference) -> ResolvedFieldType {
211 let base_type = match &*type_ref.elem {
212 Type::Path(type_path) => ResolvedFieldType {
213 type_name: Self::build_type_name(&type_path.path),
214 is_reference: true,
215 is_mutable: type_ref.mutability.is_some(),
216 generic_args: Self::extract_generic_args(&type_path.path),
217 },
218 _ => Self::unknown_field_type(),
219 };
220
221 ResolvedFieldType {
222 is_reference: true,
223 is_mutable: type_ref.mutability.is_some(),
224 ..base_type
225 }
226 }
227
228 fn build_type_name(path: &Path) -> String {
230 path.segments
231 .iter()
232 .map(|seg| seg.ident.to_string())
233 .collect::<Vec<_>>()
234 .join("::")
235 }
236
237 fn extract_generic_args(path: &Path) -> Vec<String> {
239 path.segments
240 .last()
241 .and_then(Self::extract_args_from_segment)
242 .unwrap_or_default()
243 }
244
245 fn extract_args_from_segment(segment: &PathSegment) -> Option<Vec<String>> {
247 match &segment.arguments {
248 syn::PathArguments::AngleBracketed(args) => Some(
249 args.args
250 .iter()
251 .filter_map(Self::extract_type_name_from_arg)
252 .collect(),
253 ),
254 _ => None,
255 }
256 }
257
258 fn extract_type_name_from_arg(arg: &syn::GenericArgument) -> Option<String> {
260 match arg {
261 syn::GenericArgument::Type(Type::Path(type_path)) => type_path
262 .path
263 .segments
264 .last()
265 .map(|seg| seg.ident.to_string()),
266 _ => None,
267 }
268 }
269
270 fn unknown_field_type() -> ResolvedFieldType {
272 ResolvedFieldType {
273 type_name: "Unknown".to_string(),
274 is_reference: false,
275 is_mutable: false,
276 generic_args: Vec::new(),
277 }
278 }
279
280 pub fn get_type(&self, name: &str) -> Option<&TypeDefinition> {
282 self.types.get(name)
283 }
284
285 pub fn resolve_field(&self, type_name: &str, field_name: &str) -> Option<ResolvedFieldType> {
287 let type_def = self.get_type(type_name)?;
288 let fields = type_def.fields.as_ref()?;
289 fields.named_fields.get(field_name).cloned()
290 }
291
292 pub fn resolve_tuple_field(&self, type_name: &str, index: usize) -> Option<ResolvedFieldType> {
294 let type_def = self.get_type(type_name)?;
295 let fields = type_def.fields.as_ref()?;
296 fields.tuple_fields.get(index).cloned()
297 }
298
299 pub fn add_method(&mut self, type_name: &str, method: MethodSignature) {
301 if let Some(type_def) = self.types.get_mut(type_name) {
302 type_def.methods.push(method);
303 }
304 }
305
306 pub fn register_type_alias(&mut self, alias: String, target: String) {
308 self.type_aliases.insert(alias, target);
309 }
310
311 pub fn resolve_type_alias(&self, alias: &str) -> Option<&String> {
313 self.type_aliases.get(alias)
314 }
315
316 pub fn register_imports(&mut self, file: PathBuf, imports: ImportScope) {
318 self.imports.insert(file, imports);
319 }
320
321 pub fn get_imports(&self, file: &PathBuf) -> Option<&ImportScope> {
323 self.imports.get(file)
324 }
325
326 pub fn resolve_type_with_imports(&self, file: &PathBuf, name: &str) -> Option<String> {
328 if self.types.contains_key(name) {
330 return Some(name.to_string());
331 }
332
333 if let Some(import_scope) = self.get_imports(file) {
335 if let Some(full_name) = import_scope.imports.get(name) {
337 return Some(full_name.clone());
338 }
339
340 for module_path in &import_scope.wildcard_imports {
342 let potential_name = format!("{}::{}", module_path.join("::"), name);
343 if self.types.contains_key(&potential_name) {
344 return Some(potential_name);
345 }
346 }
347 }
348
349 if let Some(target) = self.resolve_type_alias(name) {
351 return Some(target.clone());
352 }
353
354 None
355 }
356}
357
358pub fn extract_type_definitions(
360 file: &syn::File,
361 module_path: Vec<String>,
362 registry: &mut GlobalTypeRegistry,
363) {
364 for item in &file.items {
365 match item {
366 Item::Struct(item_struct) => {
367 registry.register_struct(module_path.clone(), item_struct);
368 }
369 Item::Mod(item_mod) => {
370 if let Some((_, items)) = &item_mod.content {
371 let mut nested_path = module_path.clone();
372 nested_path.push(item_mod.ident.to_string());
373 for item in items {
374 if let Item::Struct(item_struct) = item {
375 registry.register_struct(nested_path.clone(), item_struct);
376 }
377 }
378 }
379 }
380 _ => {}
381 }
382 }
383}
384
385#[cfg(test)]
386mod tests {
387 use super::*;
388 use syn::{parse_quote, Field};
389
390 #[test]
391 fn test_extract_type_name_from_simple_path() {
392 let path: syn::Path = parse_quote!(String);
393 assert_eq!(
394 GlobalTypeRegistry::extract_type_name_from_path(&path),
395 "String"
396 );
397 }
398
399 #[test]
400 fn test_extract_type_name_from_qualified_path() {
401 let path: syn::Path = parse_quote!(std::collections::HashMap);
402 assert_eq!(
403 GlobalTypeRegistry::extract_type_name_from_path(&path),
404 "std::collections::HashMap"
405 );
406 }
407
408 #[test]
409 fn test_extract_generic_args_none() {
410 let path: syn::Path = parse_quote!(String);
411 assert_eq!(
412 GlobalTypeRegistry::extract_generic_args(&path),
413 Vec::<String>::new()
414 );
415 }
416
417 #[test]
418 fn test_extract_generic_args_single() {
419 let path: syn::Path = parse_quote!(Option<String>);
420 assert_eq!(
421 GlobalTypeRegistry::extract_generic_args(&path),
422 vec!["String"]
423 );
424 }
425
426 #[test]
427 fn test_extract_generic_args_multiple() {
428 let path: syn::Path = parse_quote!(HashMap<String, Value>);
429 let args = GlobalTypeRegistry::extract_generic_args(&path);
430 assert_eq!(args.len(), 2);
431 assert!(args.contains(&"String".to_string()));
432 assert!(args.contains(&"Value".to_string()));
433 }
434
435 #[test]
436 fn test_extract_field_type_simple() {
437 let registry = GlobalTypeRegistry::new();
438 let field: Field = parse_quote!(pub name: String);
439 let field_type = registry.extract_field_type(&field);
440
441 assert_eq!(field_type.type_name, "String");
442 assert!(!field_type.is_reference);
443 assert!(!field_type.is_mutable);
444 assert!(field_type.generic_args.is_empty());
445 }
446
447 #[test]
448 fn test_extract_field_type_reference() {
449 let registry = GlobalTypeRegistry::new();
450 let field: Field = parse_quote!(pub name: &str);
451 let field_type = registry.extract_field_type(&field);
452
453 assert_eq!(field_type.type_name, "str");
454 assert!(field_type.is_reference);
455 assert!(!field_type.is_mutable);
456 }
457
458 #[test]
459 fn test_extract_field_type_mutable_reference() {
460 let registry = GlobalTypeRegistry::new();
461 let field: Field = parse_quote!(pub name: &mut String);
462 let field_type = registry.extract_field_type(&field);
463
464 assert_eq!(field_type.type_name, "String");
465 assert!(field_type.is_reference);
466 assert!(field_type.is_mutable);
467 }
468
469 #[test]
470 fn test_extract_field_type_with_generics() {
471 let registry = GlobalTypeRegistry::new();
472 let field: Field = parse_quote!(pub items: Vec<Item>);
473 let field_type = registry.extract_field_type(&field);
474
475 assert_eq!(field_type.type_name, "Vec");
476 assert_eq!(field_type.generic_args, vec!["Item"]);
477 }
478
479 #[test]
480 fn test_extract_field_type_unknown() {
481 let registry = GlobalTypeRegistry::new();
482 let field: Field = parse_quote!(pub callback: fn());
483 let field_type = registry.extract_field_type(&field);
484
485 assert_eq!(field_type.type_name, "Unknown");
486 }
487}