tauri_typegen/analysis/
struct_parser.rs

1use crate::analysis::type_resolver::TypeResolver;
2use crate::analysis::validator_parser::ValidatorParser;
3use crate::models::{FieldInfo, StructInfo};
4use quote::ToTokens;
5use std::path::Path;
6use syn::{Attribute, ItemEnum, ItemStruct, Type, Visibility};
7
8/// Parser for Rust structs and enums
9#[derive(Debug)]
10pub struct StructParser {
11    validator_parser: ValidatorParser,
12}
13
14impl StructParser {
15    pub fn new() -> Self {
16        Self {
17            validator_parser: ValidatorParser::new(),
18        }
19    }
20
21    /// Check if a struct should be included in type generation
22    pub fn should_include_struct(&self, item_struct: &ItemStruct) -> bool {
23        // Check if struct has Serialize or Deserialize derive
24        for attr in &item_struct.attrs {
25            if self.should_include(attr) {
26                return true;
27            }
28        }
29        false
30    }
31
32    /// Check if an enum should be included in type generation
33    pub fn should_include_enum(&self, item_enum: &ItemEnum) -> bool {
34        // Check if enum has Serialize or Deserialize derive
35        for attr in &item_enum.attrs {
36            if self.should_include(attr) {
37                return true;
38            }
39        }
40        false
41    }
42
43    /// Check if an attribute indicates the type should be included
44    fn should_include(&self, attr: &Attribute) -> bool {
45        if let Ok(meta_list) = attr.meta.require_list() {
46            if meta_list.path.is_ident("derive") {
47                let tokens_str = meta_list.to_token_stream().to_string();
48
49                tokens_str.contains("Serialize") || tokens_str.contains("Deserialize")
50            } else {
51                false
52            }
53        } else {
54            false
55        }
56    }
57
58    /// Parse a Rust struct into StructInfo
59    pub fn parse_struct(
60        &self,
61        item_struct: &ItemStruct,
62        file_path: &Path,
63        type_resolver: &mut TypeResolver,
64    ) -> Option<StructInfo> {
65        let fields = match &item_struct.fields {
66            syn::Fields::Named(fields_named) => fields_named
67                .named
68                .iter()
69                .filter_map(|field| self.parse_field(field, type_resolver))
70                .collect(),
71            syn::Fields::Unnamed(_) => {
72                // Handle tuple structs if needed
73                return None;
74            }
75            syn::Fields::Unit => {
76                // Unit struct
77                Vec::new()
78            }
79        };
80
81        Some(StructInfo {
82            name: item_struct.ident.to_string(),
83            fields,
84            file_path: file_path.to_string_lossy().to_string(),
85            is_enum: false,
86        })
87    }
88
89    /// Parse a Rust enum into StructInfo
90    pub fn parse_enum(
91        &self,
92        item_enum: &ItemEnum,
93        file_path: &Path,
94        type_resolver: &mut TypeResolver,
95    ) -> Option<StructInfo> {
96        let fields = item_enum
97            .variants
98            .iter()
99            .map(|variant| {
100                match &variant.fields {
101                    syn::Fields::Unit => {
102                        // Unit variant: Variant
103                        FieldInfo {
104                            name: variant.ident.to_string(),
105                            rust_type: "enum_variant".to_string(),
106                            typescript_type: format!("\"{}\"", variant.ident),
107                            is_optional: false,
108                            is_public: true,
109                            validator_attributes: None,
110                        }
111                    }
112                    syn::Fields::Unnamed(fields_unnamed) => {
113                        // Tuple variant: Variant(T, U)
114                        let types: Vec<String> = fields_unnamed
115                            .unnamed
116                            .iter()
117                            .map(|field| {
118                                type_resolver
119                                    .map_rust_type_to_typescript(&self.type_to_string(&field.ty))
120                            })
121                            .collect();
122                        FieldInfo {
123                            name: variant.ident.to_string(),
124                            rust_type: "enum_variant_tuple".to_string(),
125                            typescript_type: format!(
126                                "{{ type: \"{}\", data: [{}] }}",
127                                variant.ident,
128                                types.join(", ")
129                            ),
130                            is_optional: false,
131                            is_public: true,
132                            validator_attributes: None,
133                        }
134                    }
135                    syn::Fields::Named(fields_named) => {
136                        // Struct variant: Variant { field: T }
137                        let struct_fields: Vec<String> = fields_named
138                            .named
139                            .iter()
140                            .filter_map(|field| {
141                                field.ident.as_ref().map(|field_name| {
142                                    let field_type = type_resolver.map_rust_type_to_typescript(
143                                        &self.type_to_string(&field.ty),
144                                    );
145                                    format!("{}: {}", field_name, field_type)
146                                })
147                            })
148                            .collect();
149                        FieldInfo {
150                            name: variant.ident.to_string(),
151                            rust_type: "enum_variant_struct".to_string(),
152                            typescript_type: format!(
153                                "{{ type: \"{}\", data: {{ {} }} }}",
154                                variant.ident,
155                                struct_fields.join(", ")
156                            ),
157                            is_optional: false,
158                            is_public: true,
159                            validator_attributes: None,
160                        }
161                    }
162                }
163            })
164            .collect();
165
166        Some(StructInfo {
167            name: item_enum.ident.to_string(),
168            fields,
169            file_path: file_path.to_string_lossy().to_string(),
170            is_enum: true,
171        })
172    }
173
174    /// Parse a struct field into FieldInfo
175    fn parse_field(
176        &self,
177        field: &syn::Field,
178        type_resolver: &mut TypeResolver,
179    ) -> Option<FieldInfo> {
180        let name = field.ident.as_ref()?.to_string();
181        let is_public = matches!(field.vis, Visibility::Public(_));
182        let is_optional = self.is_optional_type(&field.ty);
183        let rust_type = self.type_to_string(&field.ty);
184        let typescript_type = type_resolver.map_rust_type_to_typescript(&rust_type);
185        let validator_attributes = self
186            .validator_parser
187            .parse_validator_attributes(&field.attrs);
188
189        Some(FieldInfo {
190            name,
191            rust_type,
192            typescript_type,
193            is_optional,
194            is_public,
195            validator_attributes,
196        })
197    }
198
199    /// Check if a type is Option<T>
200    fn is_optional_type(&self, ty: &Type) -> bool {
201        if let Type::Path(type_path) = ty {
202            if let Some(segment) = type_path.path.segments.last() {
203                segment.ident == "Option"
204            } else {
205                false
206            }
207        } else {
208            false
209        }
210    }
211
212    /// Convert a Type to its string representation
213    fn type_to_string(&self, ty: &Type) -> String {
214        match ty {
215            Type::Path(type_path) => {
216                let path = &type_path.path;
217                let segments: Vec<String> = path
218                    .segments
219                    .iter()
220                    .map(|segment| {
221                        let ident = segment.ident.to_string();
222                        match &segment.arguments {
223                            syn::PathArguments::None => ident,
224                            syn::PathArguments::AngleBracketed(args) => {
225                                let generic_args: Vec<String> = args
226                                    .args
227                                    .iter()
228                                    .map(|arg| match arg {
229                                        syn::GenericArgument::Type(t) => self.type_to_string(t),
230                                        _ => "unknown".to_string(),
231                                    })
232                                    .collect();
233                                format!("{}<{}>", ident, generic_args.join(", "))
234                            }
235                            syn::PathArguments::Parenthesized(_) => ident, // Function types, not common in structs
236                        }
237                    })
238                    .collect();
239                segments.join("::")
240            }
241            Type::Reference(type_ref) => {
242                format!("&{}", self.type_to_string(&type_ref.elem))
243            }
244            Type::Tuple(type_tuple) => {
245                let elements: Vec<String> = type_tuple
246                    .elems
247                    .iter()
248                    .map(|elem| self.type_to_string(elem))
249                    .collect();
250                format!("({})", elements.join(", "))
251            }
252            Type::Array(type_array) => {
253                format!("[{}; _]", self.type_to_string(&type_array.elem))
254            }
255            Type::Slice(type_slice) => {
256                format!("[{}]", self.type_to_string(&type_slice.elem))
257            }
258            _ => "unknown".to_string(),
259        }
260    }
261}
262
263impl Default for StructParser {
264    fn default() -> Self {
265        Self::new()
266    }
267}