tauri_typegen/analysis/
serde_parser.rs1use serde_rename_rule::RenameRule;
2use syn::{Attribute, Expr, ExprLit, Lit};
3
4#[derive(Debug)]
6pub struct SerdeParser;
7
8impl SerdeParser {
9 pub fn new() -> Self {
10 Self
11 }
12
13 pub fn parse_struct_serde_attrs(&self, attrs: &[Attribute]) -> SerdeStructAttributes {
15 let mut result = SerdeStructAttributes {
16 rename_all: None,
17 tag: None,
18 content: None,
19 };
20
21 for attr in attrs {
22 if attr.path().is_ident("serde") {
23 let _ = attr.parse_nested_meta(|meta| {
24 if meta.path.is_ident("rename_all") {
25 if let Some(value) = parse_string_value(&meta)? {
26 result.rename_all = RenameRule::from_rename_all_str(&value).ok();
27 }
28 } else if meta.path.is_ident("tag") {
29 result.tag = parse_string_value(&meta)?;
30 } else if meta.path.is_ident("content") {
31 result.content = parse_string_value(&meta)?;
32 }
33 Ok(())
34 });
35 }
36 }
37
38 result
39 }
40
41 pub fn parse_field_serde_attrs(&self, attrs: &[Attribute]) -> SerdeFieldAttributes {
43 let mut result = SerdeFieldAttributes {
44 rename: None,
45 skip: false,
46 };
47
48 for attr in attrs {
49 if attr.path().is_ident("serde") {
50 let _ = attr.parse_nested_meta(|meta| {
51 if meta.path.is_ident("rename") {
52 result.rename = parse_string_value(&meta)?;
53 } else if meta.path.is_ident("skip") {
54 result.skip = true;
56 }
57 Ok(())
60 });
61 }
62 }
63
64 result
65 }
66}
67
68fn parse_string_value(meta: &syn::meta::ParseNestedMeta) -> syn::Result<Option<String>> {
70 let expr: Expr = meta.value()?.parse()?;
71 if let Expr::Lit(ExprLit {
72 lit: Lit::Str(lit_str),
73 ..
74 }) = expr
75 {
76 Ok(Some(lit_str.value()))
77 } else {
78 Ok(None)
79 }
80}
81
82impl Default for SerdeParser {
83 fn default() -> Self {
84 Self::new()
85 }
86}
87
88#[derive(Debug, Default, Clone)]
90pub struct SerdeStructAttributes {
91 pub rename_all: Option<RenameRule>,
92 pub tag: Option<String>,
94 pub content: Option<String>,
96}
97
98#[derive(Debug, Default, Clone)]
100pub struct SerdeFieldAttributes {
101 pub rename: Option<String>,
102 pub skip: bool,
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108 use syn::parse_quote;
109
110 #[test]
111 fn test_parse_struct_serde_attrs_with_rename_all_camel_case() {
112 let parser = SerdeParser::new();
113 let attrs: Vec<Attribute> = vec![parse_quote!(#[serde(rename_all = "camelCase")])];
114
115 let result = parser.parse_struct_serde_attrs(&attrs);
116 assert!(result.rename_all.is_some());
117 assert!(matches!(result.rename_all.unwrap(), RenameRule::CamelCase));
118 }
119
120 #[test]
121 fn test_parse_struct_serde_attrs_with_rename_all_snake_case() {
122 let parser = SerdeParser::new();
123 let attrs: Vec<Attribute> = vec![parse_quote!(#[serde(rename_all = "snake_case")])];
124
125 let result = parser.parse_struct_serde_attrs(&attrs);
126 assert!(result.rename_all.is_some());
127 assert!(matches!(result.rename_all.unwrap(), RenameRule::SnakeCase));
128 }
129
130 #[test]
131 fn test_parse_struct_serde_attrs_with_rename_all_pascal_case() {
132 let parser = SerdeParser::new();
133 let attrs: Vec<Attribute> = vec![parse_quote!(#[serde(rename_all = "PascalCase")])];
134
135 let result = parser.parse_struct_serde_attrs(&attrs);
136 assert!(result.rename_all.is_some());
137 assert!(matches!(result.rename_all.unwrap(), RenameRule::PascalCase));
138 }
139
140 #[test]
141 fn test_parse_struct_serde_attrs_with_rename_all_screaming_snake_case() {
142 let parser = SerdeParser::new();
143 let attrs: Vec<Attribute> =
144 vec![parse_quote!(#[serde(rename_all = "SCREAMING_SNAKE_CASE")])];
145
146 let result = parser.parse_struct_serde_attrs(&attrs);
147 assert!(result.rename_all.is_some());
148 assert!(matches!(
149 result.rename_all.unwrap(),
150 RenameRule::ScreamingSnakeCase
151 ));
152 }
153
154 #[test]
155 fn test_parse_struct_serde_attrs_with_rename_all_kebab_case() {
156 let parser = SerdeParser::new();
157 let attrs: Vec<Attribute> = vec![parse_quote!(#[serde(rename_all = "kebab-case")])];
158
159 let result = parser.parse_struct_serde_attrs(&attrs);
160 assert!(result.rename_all.is_some());
161 assert!(matches!(result.rename_all.unwrap(), RenameRule::KebabCase));
162 }
163
164 #[test]
165 fn test_parse_struct_serde_attrs_no_serde() {
166 let parser = SerdeParser::new();
167 let attrs: Vec<Attribute> = vec![parse_quote!(#[derive(Debug)])];
168
169 let result = parser.parse_struct_serde_attrs(&attrs);
170 assert!(result.rename_all.is_none());
171 assert!(result.tag.is_none());
172 assert!(result.content.is_none());
173 }
174
175 #[test]
176 fn test_parse_struct_serde_attrs_with_tag() {
177 let parser = SerdeParser::new();
178 let attrs: Vec<Attribute> = vec![parse_quote!(#[serde(tag = "type")])];
179
180 let result = parser.parse_struct_serde_attrs(&attrs);
181 assert_eq!(result.tag, Some("type".to_string()));
182 }
183
184 #[test]
185 fn test_parse_struct_serde_attrs_with_custom_tag() {
186 let parser = SerdeParser::new();
187 let attrs: Vec<Attribute> = vec![parse_quote!(#[serde(tag = "kind")])];
188
189 let result = parser.parse_struct_serde_attrs(&attrs);
190 assert_eq!(result.tag, Some("kind".to_string()));
191 }
192
193 #[test]
194 fn test_parse_struct_serde_attrs_with_content() {
195 let parser = SerdeParser::new();
196 let attrs: Vec<Attribute> = vec![parse_quote!(#[serde(content = "data")])];
197
198 let result = parser.parse_struct_serde_attrs(&attrs);
199 assert_eq!(result.content, Some("data".to_string()));
200 }
201
202 #[test]
203 fn test_parse_struct_serde_attrs_with_tag_and_content() {
204 let parser = SerdeParser::new();
205 let attrs: Vec<Attribute> = vec![parse_quote!(#[serde(tag = "kind", content = "data")])];
206
207 let result = parser.parse_struct_serde_attrs(&attrs);
208 assert_eq!(result.tag, Some("kind".to_string()));
209 assert_eq!(result.content, Some("data".to_string()));
210 }
211
212 #[test]
213 fn test_parse_struct_serde_attrs_with_all_attributes() {
214 let parser = SerdeParser::new();
215 let attrs: Vec<Attribute> =
216 vec![parse_quote!(#[serde(rename_all = "camelCase", tag = "type", content = "value")])];
217
218 let result = parser.parse_struct_serde_attrs(&attrs);
219 assert!(matches!(result.rename_all, Some(RenameRule::CamelCase)));
220 assert_eq!(result.tag, Some("type".to_string()));
221 assert_eq!(result.content, Some("value".to_string()));
222 }
223
224 #[test]
225 fn test_parse_field_serde_attrs_with_rename() {
226 let parser = SerdeParser::new();
227 let attrs: Vec<Attribute> = vec![parse_quote!(#[serde(rename = "customName")])];
228
229 let result = parser.parse_field_serde_attrs(&attrs);
230 assert_eq!(result.rename, Some("customName".to_string()));
231 assert!(!result.skip);
232 }
233
234 #[test]
235 fn test_parse_field_serde_attrs_with_skip() {
236 let parser = SerdeParser::new();
237 let attrs: Vec<Attribute> = vec![parse_quote!(#[serde(skip)])];
238
239 let result = parser.parse_field_serde_attrs(&attrs);
240 assert!(result.skip);
241 assert!(result.rename.is_none());
242 }
243
244 #[test]
245 fn test_parse_field_serde_attrs_skip_serializing_not_skip() {
246 let parser = SerdeParser::new();
247 let attrs: Vec<Attribute> = vec![parse_quote!(#[serde(skip_serializing)])];
248
249 let result = parser.parse_field_serde_attrs(&attrs);
250 assert!(!result.skip);
252 }
253
254 #[test]
255 fn test_parse_field_serde_attrs_skip_deserializing_not_skip() {
256 let parser = SerdeParser::new();
257 let attrs: Vec<Attribute> = vec![parse_quote!(#[serde(skip_deserializing)])];
258
259 let result = parser.parse_field_serde_attrs(&attrs);
260 assert!(!result.skip);
262 }
263
264 #[test]
265 fn test_parse_field_serde_attrs_multiple_attributes() {
266 let parser = SerdeParser::new();
267 let attrs: Vec<Attribute> = vec![
268 parse_quote!(#[serde(rename = "id")]),
269 parse_quote!(#[derive(Debug)]),
270 ];
271
272 let result = parser.parse_field_serde_attrs(&attrs);
273 assert_eq!(result.rename, Some("id".to_string()));
274 }
275
276 #[test]
277 fn test_parse_field_serde_attrs_rename_and_skip() {
278 let parser = SerdeParser::new();
279 let attrs: Vec<Attribute> = vec![parse_quote!(#[serde(rename = "id", skip)])];
280
281 let result = parser.parse_field_serde_attrs(&attrs);
282 assert_eq!(result.rename, Some("id".to_string()));
283 assert!(result.skip);
284 }
285
286 #[test]
287 fn test_parse_field_serde_attrs_no_serde() {
288 let parser = SerdeParser::new();
289 let attrs: Vec<Attribute> = vec![parse_quote!(#[derive(Debug)])];
290
291 let result = parser.parse_field_serde_attrs(&attrs);
292 assert!(result.rename.is_none());
293 assert!(!result.skip);
294 }
295
296 #[test]
297 fn test_parse_field_serde_attrs_empty() {
298 let parser = SerdeParser::new();
299 let attrs: Vec<Attribute> = vec![];
300
301 let result = parser.parse_field_serde_attrs(&attrs);
302 assert!(result.rename.is_none());
303 assert!(!result.skip);
304 }
305
306 #[test]
307 fn test_default_impl() {
308 let parser = SerdeParser::new();
309 let attrs: Vec<Attribute> = vec![parse_quote!(#[serde(rename = "test")])];
310 let result = parser.parse_field_serde_attrs(&attrs);
311 assert_eq!(result.rename, Some("test".to_string()));
312 }
313
314 #[test]
315 fn test_parse_struct_serde_attrs_ignores_other_attributes() {
316 let parser = SerdeParser::new();
317 let attrs: Vec<Attribute> = vec![parse_quote!(
318 #[serde(rename_all = "camelCase", deny_unknown_fields)]
319 )];
320
321 let result = parser.parse_struct_serde_attrs(&attrs);
322 assert!(matches!(result.rename_all, Some(RenameRule::CamelCase)));
323 }
325
326 #[test]
327 fn test_parse_field_serde_attrs_ignores_other_attributes() {
328 let parser = SerdeParser::new();
329 let attrs: Vec<Attribute> = vec![
330 parse_quote!(#[serde(rename = "id", default, skip_serializing_if = "Option::is_none")]),
331 ];
332
333 let result = parser.parse_field_serde_attrs(&attrs);
334 assert_eq!(result.rename, Some("id".to_string()));
335 }
337
338 #[test]
339 fn test_parse_multiple_serde_attributes() {
340 let parser = SerdeParser::new();
341 let attrs: Vec<Attribute> = vec![
342 parse_quote!(#[serde(rename_all = "camelCase")]),
343 parse_quote!(#[serde(tag = "type")]),
344 ];
345
346 let result = parser.parse_struct_serde_attrs(&attrs);
347 assert!(matches!(result.rename_all, Some(RenameRule::CamelCase)));
348 assert_eq!(result.tag, Some("type".to_string()));
349 }
350}