tauri_typegen/analysis/
struct_parser.rs

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