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#[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 pub fn should_include_struct(&self, item_struct: &ItemStruct) -> bool {
26 for attr in &item_struct.attrs {
28 if self.should_include(attr) {
29 return true;
30 }
31 }
32 false
33 }
34
35 pub fn should_include_enum(&self, item_enum: &ItemEnum) -> bool {
37 for attr in &item_enum.attrs {
39 if self.should_include(attr) {
40 return true;
41 }
42 }
43 false
44 }
45
46 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 pub fn parse_struct(
63 &self,
64 item_struct: &ItemStruct,
65 file_path: &Path,
66 type_resolver: &mut TypeResolver,
67 ) -> Option<StructInfo> {
68 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 return None;
88 }
89 syn::Fields::Unit => {
90 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 pub fn parse_enum(
105 &self,
106 item_enum: &ItemEnum,
107 file_path: &Path,
108 type_resolver: &mut TypeResolver,
109 ) -> Option<StructInfo> {
110 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 let variant_serde_attrs = self.serde_parser.parse_field_serde_attrs(&variant.attrs);
121
122 let serialized_name = if let Some(rename) = variant_serde_attrs.rename {
124 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 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 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 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 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 let field_serde_attrs = self.serde_parser.parse_field_serde_attrs(&field.attrs);
221
222 if field_serde_attrs.skip {
224 return None;
225 }
226
227 let serialized_name = if let Some(rename) = field_serde_attrs.rename {
229 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 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 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, }
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}