tauri_typegen/analysis/
struct_parser.rs1use 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#[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 pub fn should_include_struct(&self, item_struct: &ItemStruct) -> bool {
23 for attr in &item_struct.attrs {
25 if self.should_include(attr) {
26 return true;
27 }
28 }
29 false
30 }
31
32 pub fn should_include_enum(&self, item_enum: &ItemEnum) -> bool {
34 for attr in &item_enum.attrs {
36 if self.should_include(attr) {
37 return true;
38 }
39 }
40 false
41 }
42
43 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 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 return None;
74 }
75 syn::Fields::Unit => {
76 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 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 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 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 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 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 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 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, }
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}