1use std::collections::{HashMap, HashSet};
7use std::fs;
8use std::io;
9use std::path::Path;
10
11use syn::{Item, Type, FnArg, Pat, ReturnType, Fields, Visibility};
12use quote::ToTokens;
13
14use crate::unified_type::UnifiedType;
15
16#[derive(Debug, Clone)]
18pub struct RustConst {
19 pub name: String,
20 pub ty: String,
21 pub uty: UnifiedType,
22}
23
24#[derive(Debug, Clone)]
26pub struct RustParam {
27 pub name: String,
28 pub ty: String,
29 pub uty: UnifiedType,
30}
31
32#[derive(Debug, Clone)]
34pub struct RustFn {
35 pub name: String,
36 pub params: Vec<RustParam>,
37 pub ret_ty: Option<String>,
38 pub uret_ty: Option<UnifiedType>,
39}
40
41#[derive(Debug, Clone)]
43pub struct RustField {
44 pub name: String,
45 pub ty: String,
46 pub uty: UnifiedType,
47}
48
49#[derive(Debug, Clone)]
51pub struct RustStruct {
52 pub name: String,
53 pub fields: Vec<RustField>,
54}
55
56#[derive(Debug, Clone)]
58pub struct RustTypeAlias {
59 pub name: String,
60 pub ty: String,
61 pub uty: UnifiedType,
62}
63
64#[derive(Debug, Default)]
66pub struct RustDeclDict {
67 pub consts: HashMap<String, RustConst>,
68 pub fns: HashMap<String, RustFn>,
69 pub structs: HashMap<String, RustStruct>,
70 pub types: HashMap<String, RustTypeAlias>,
71 pub enums: HashSet<String>,
72 pub statics: HashSet<String>,
74 pub static_arrays: HashSet<String>,
76 pub static_types: HashMap<String, String>,
79 pub bitfield_methods: HashMap<String, HashSet<String>>,
81 pub bitfield_method_types: HashMap<(String, String), String>,
85}
86
87impl RustDeclDict {
88 pub fn new() -> Self {
90 Self::default()
91 }
92
93 pub fn parse_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
95 let content = fs::read_to_string(path)?;
96 Ok(Self::parse(&content))
97 }
98
99 pub fn intern_names(&self, interner: &mut crate::intern::StringInterner) {
107 for name in self.structs.keys() {
108 interner.intern(name);
109 }
110 for name in self.types.keys() {
111 interner.intern(name);
112 }
113 for name in &self.enums {
114 interner.intern(name);
115 }
116 }
117
118 pub fn parse(content: &str) -> Self {
120 let mut dict = Self::new();
121
122 let file = match syn::parse_file(content) {
124 Ok(f) => f,
125 Err(e) => {
126 eprintln!("Warning: Failed to parse Rust file: {}", e);
127 return dict;
128 }
129 };
130
131 for item in file.items {
133 dict.process_item(&item);
134 }
135
136 dict
137 }
138
139 fn process_item(&mut self, item: &Item) {
141 match item {
142 Item::Const(item_const) => {
143 if Self::is_pub(&item_const.vis) {
144 let name = item_const.ident.to_string();
145 let uty = UnifiedType::from_syn_type(&item_const.ty);
146 let ty = Self::type_to_string(&item_const.ty);
147 self.consts.insert(name.clone(), RustConst { name, ty, uty });
148 }
149 }
150 Item::Type(item_type) => {
151 if Self::is_pub(&item_type.vis) {
152 let name = item_type.ident.to_string();
153 let uty = UnifiedType::from_syn_type(&item_type.ty);
154 let ty = Self::type_to_string(&item_type.ty);
155 self.types.insert(name.clone(), RustTypeAlias { name, ty, uty });
156 }
157 }
158 Item::Struct(item_struct) => {
159 if Self::is_pub(&item_struct.vis) {
160 let name = item_struct.ident.to_string();
161 let fields = Self::extract_fields(&item_struct.fields);
162 self.structs.insert(name.clone(), RustStruct { name, fields });
163 }
164 }
165 Item::Union(item_union) => {
166 if Self::is_pub(&item_union.vis) {
167 let name = item_union.ident.to_string();
168 let fields = Self::extract_fields(&Fields::Named(item_union.fields.clone()));
169 self.structs.insert(name.clone(), RustStruct { name, fields });
170 }
171 }
172 Item::Fn(item_fn) => {
173 if Self::is_pub(&item_fn.vis) {
174 if let Some(rust_fn) = Self::extract_fn(&item_fn.sig) {
175 self.fns.insert(rust_fn.name.clone(), rust_fn);
176 }
177 }
178 }
179 Item::ForeignMod(foreign_mod) => {
180 for foreign_item in &foreign_mod.items {
182 match foreign_item {
183 syn::ForeignItem::Fn(fn_item) => {
184 if Self::is_pub(&fn_item.vis) {
185 if let Some(rust_fn) = Self::extract_fn(&fn_item.sig) {
186 self.fns.insert(rust_fn.name.clone(), rust_fn);
187 }
188 }
189 }
190 syn::ForeignItem::Static(static_item) => {
191 let name = static_item.ident.to_string();
192 let ty_str = Self::type_to_string(&static_item.ty);
193 self.statics.insert(name.clone());
194 if ty_str.starts_with("[") {
195 self.static_arrays.insert(name.clone());
196 }
197 self.static_types.insert(name, ty_str);
198 }
199 _ => {}
200 }
201 }
202 }
203 Item::Enum(item_enum) => {
204 if Self::is_pub(&item_enum.vis) {
205 self.enums.insert(item_enum.ident.to_string());
206 }
207 }
208 Item::Impl(item_impl) => {
209 let struct_name = Self::type_to_string(&item_impl.self_ty);
211 for impl_item in &item_impl.items {
212 if let syn::ImplItem::Fn(method) = impl_item {
213 let body_str = method.block.to_token_stream().to_string();
214 if Self::has_self_receiver(&method.sig)
215 && body_str.contains("_bitfield_")
216 && method.sig.inputs.len() == 1 {
218 let method_name = method.sig.ident.to_string();
219 if let syn::ReturnType::Type(_, ty) = &method.sig.output {
221 let ret_ty = Self::type_to_string(ty);
222 self.bitfield_method_types
223 .insert((struct_name.clone(), method_name.clone()), ret_ty);
224 }
225 self.bitfield_methods
226 .entry(struct_name.clone())
227 .or_default()
228 .insert(method_name);
229 }
230 }
231 }
232 }
233 _ => {}
234 }
235 }
236
237 fn is_pub(vis: &Visibility) -> bool {
239 matches!(vis, Visibility::Public(_))
240 }
241
242 fn has_self_receiver(sig: &syn::Signature) -> bool {
244 sig.inputs.first().is_some_and(|arg| matches!(arg, FnArg::Receiver(_)))
245 }
246
247 fn type_to_string(ty: &Type) -> String {
249 ty.to_token_stream().to_string()
250 }
251
252 fn extract_fields(fields: &Fields) -> Vec<RustField> {
254 let mut result = Vec::new();
255
256 match fields {
257 Fields::Named(named) => {
258 for field in &named.named {
259 if Self::is_pub(&field.vis) {
260 if let Some(ident) = &field.ident {
261 let uty = UnifiedType::from_syn_type(&field.ty);
262 let ty = Self::type_to_string(&field.ty);
263 result.push(RustField {
264 name: ident.to_string(),
265 ty,
266 uty,
267 });
268 }
269 }
270 }
271 }
272 Fields::Unnamed(unnamed) => {
273 for (i, field) in unnamed.unnamed.iter().enumerate() {
274 if Self::is_pub(&field.vis) {
275 let uty = UnifiedType::from_syn_type(&field.ty);
276 let ty = Self::type_to_string(&field.ty);
277 result.push(RustField {
278 name: format!("{}", i),
279 ty,
280 uty,
281 });
282 }
283 }
284 }
285 Fields::Unit => {}
286 }
287
288 result
289 }
290
291 fn extract_fn(sig: &syn::Signature) -> Option<RustFn> {
293 let name = sig.ident.to_string();
294
295 let mut params = Vec::new();
296 for arg in &sig.inputs {
297 match arg {
298 FnArg::Receiver(_) => {
299 }
301 FnArg::Typed(pat_type) => {
302 let param_name = match pat_type.pat.as_ref() {
303 Pat::Ident(pat_ident) => pat_ident.ident.to_string(),
304 _ => "_".to_string(),
305 };
306 let uty = UnifiedType::from_syn_type(&pat_type.ty);
307 let param_ty = Self::type_to_string(&pat_type.ty);
308 params.push(RustParam {
309 name: param_name,
310 ty: param_ty,
311 uty,
312 });
313 }
314 }
315 }
316
317 let (ret_ty, uret_ty) = match &sig.output {
318 ReturnType::Default => (None, None),
319 ReturnType::Type(_, ty) => (
320 Some(Self::type_to_string(ty)),
321 Some(UnifiedType::from_syn_type(ty)),
322 ),
323 };
324
325 Some(RustFn {
326 name,
327 params,
328 ret_ty,
329 uret_ty,
330 })
331 }
332
333 pub fn stats(&self) -> RustDeclStats {
335 RustDeclStats {
336 const_count: self.consts.len(),
337 fn_count: self.fns.len(),
338 struct_count: self.structs.len(),
339 type_count: self.types.len(),
340 }
341 }
342
343 pub fn thx_functions(&self) -> std::collections::HashSet<String> {
347 self.fns.iter()
348 .filter(|(_, f)| {
349 f.params.first()
350 .map(|p| p.ty.contains("PerlInterpreter"))
351 .unwrap_or(false)
352 })
353 .map(|(name, _)| name.clone())
354 .collect()
355 }
356
357 pub fn dump(&self) -> String {
359 let mut result = String::new();
360
361 result.push_str("=== Constants ===\n");
362 let mut consts: Vec<_> = self.consts.values().collect();
363 consts.sort_by_key(|c| &c.name);
364 for c in consts {
365 result.push_str(&format!(" {}: {}\n", c.name, c.ty));
366 }
367
368 result.push_str("\n=== Type Aliases ===\n");
369 let mut types: Vec<_> = self.types.values().collect();
370 types.sort_by_key(|t| &t.name);
371 for t in types {
372 result.push_str(&format!(" {} = {}\n", t.name, t.ty));
373 }
374
375 result.push_str("\n=== Functions ===\n");
376 let mut fns: Vec<_> = self.fns.values().collect();
377 fns.sort_by_key(|f| &f.name);
378 for f in fns {
379 let params: Vec<_> = f.params.iter().map(|p| format!("{}: {}", p.name, p.ty)).collect();
380 let ret = f.ret_ty.as_deref().unwrap_or("()");
381 result.push_str(&format!(" fn {}({}) -> {}\n", f.name, params.join(", "), ret));
382 }
383
384 result.push_str("\n=== Structs ===\n");
385 let mut structs: Vec<_> = self.structs.values().collect();
386 structs.sort_by_key(|s| &s.name);
387 for s in structs {
388 result.push_str(&format!(" struct {} {{\n", s.name));
389 for f in &s.fields {
390 result.push_str(&format!(" {}: {},\n", f.name, f.ty));
391 }
392 result.push_str(" }\n");
393 }
394
395 result
396 }
397
398 pub fn lookup_const(&self, name: &str) -> Option<&RustConst> {
400 self.consts.get(name)
401 }
402
403 pub fn lookup_fn(&self, name: &str) -> Option<&RustFn> {
405 self.fns.get(name)
406 }
407
408 pub fn lookup_struct(&self, name: &str) -> Option<&RustStruct> {
410 self.structs.get(name)
411 }
412
413 pub fn lookup_type(&self, name: &str) -> Option<&RustTypeAlias> {
415 self.types.get(name)
416 }
417}
418
419#[derive(Debug)]
421pub struct RustDeclStats {
422 pub const_count: usize,
423 pub fn_count: usize,
424 pub struct_count: usize,
425 pub type_count: usize,
426}
427
428#[cfg(test)]
429mod tests {
430 use super::*;
431
432 #[test]
433 fn test_parse_const() {
434 let dict = RustDeclDict::parse("pub const FOO: u32 = 42;");
435 assert_eq!(dict.consts.len(), 1);
436 let c = dict.consts.get("FOO").unwrap();
437 assert_eq!(c.name, "FOO");
438 assert_eq!(c.ty, "u32");
439 }
440
441 #[test]
442 fn test_parse_const_array() {
443 let dict = RustDeclDict::parse("pub const MSG: &[u8; 10] = b\"(unknown)\\0\";");
444 let c = dict.consts.get("MSG").unwrap();
445 assert_eq!(c.name, "MSG");
446 assert_eq!(c.ty, "& [u8 ; 10]");
447 }
448
449 #[test]
450 fn test_parse_type_alias() {
451 let dict = RustDeclDict::parse("pub type Size = ::std::os::raw::c_ulong;");
452 let t = dict.types.get("Size").unwrap();
453 assert_eq!(t.name, "Size");
454 assert_eq!(t.ty, ":: std :: os :: raw :: c_ulong");
455 }
456
457 #[test]
458 fn test_parse_fn() {
459 let dict = RustDeclDict::parse(r#"
460 extern "C" {
461 pub fn foo(x: i32, y: *mut u8) -> bool;
462 }
463 "#);
464 let f = dict.fns.get("foo").unwrap();
465 assert_eq!(f.name, "foo");
466 assert_eq!(f.params.len(), 2);
467 assert_eq!(f.params[0].name, "x");
468 assert_eq!(f.params[0].ty, "i32");
469 assert_eq!(f.params[1].name, "y");
470 assert_eq!(f.params[1].ty, "* mut u8");
471 assert_eq!(f.ret_ty, Some("bool".to_string()));
472 }
473
474 #[test]
475 fn test_parse_fn_no_return() {
476 let dict = RustDeclDict::parse(r#"
477 extern "C" {
478 pub fn bar(x: i32);
479 }
480 "#);
481 let f = dict.fns.get("bar").unwrap();
482 assert_eq!(f.name, "bar");
483 assert_eq!(f.ret_ty, None);
484 }
485
486 #[test]
487 fn test_parse_struct() {
488 let content = r#"
489pub struct Point {
490 pub x: i32,
491 pub y: i32,
492}
493"#;
494 let dict = RustDeclDict::parse(content);
495 let s = dict.structs.get("Point").unwrap();
496 assert_eq!(s.name, "Point");
497 assert_eq!(s.fields.len(), 2);
498 assert_eq!(s.fields[0].name, "x");
499 assert_eq!(s.fields[0].ty, "i32");
500 }
501
502 #[test]
503 fn test_parse_struct_with_option() {
504 let content = r#"
505pub struct Test {
506 pub callback: ::std::option::Option<
507 unsafe extern "C" fn(x: i32) -> i32,
508 >,
509}
510"#;
511 let dict = RustDeclDict::parse(content);
512 let s = dict.structs.get("Test").unwrap();
513 assert_eq!(s.name, "Test");
514 assert_eq!(s.fields.len(), 1);
515 assert_eq!(s.fields[0].name, "callback");
516 assert!(s.fields[0].ty.contains("Option"));
518 assert!(s.fields[0].ty.contains("fn"));
519 assert!(s.fields[0].ty.ends_with(">"), "Type should end with >");
520 }
521
522 #[test]
523 fn test_parse_struct_with_generics() {
524 let content = r#"
525pub struct Wrapper<T> {
526 pub value: T,
527}
528"#;
529 let dict = RustDeclDict::parse(content);
530 let s = dict.structs.get("Wrapper").unwrap();
531 assert_eq!(s.name, "Wrapper");
532 }
533}