tauri_typegen/analysis/
serde_parser.rs1use quote::ToTokens;
2use serde_rename_rule::RenameRule;
3use syn::Attribute;
4
5#[derive(Debug)]
7pub struct SerdeParser;
8
9impl SerdeParser {
10 pub fn new() -> Self {
11 Self
12 }
13
14 pub fn parse_struct_serde_attrs(&self, attrs: &[Attribute]) -> SerdeStructAttributes {
16 let mut result = SerdeStructAttributes { rename_all: None };
17
18 for attr in attrs {
19 if attr.path().is_ident("serde") {
20 if let Ok(tokens) = syn::parse2::<syn::MetaList>(attr.meta.to_token_stream()) {
21 let tokens_str = tokens.tokens.to_string();
22
23 if let Some(convention) = self.parse_rename_all(&tokens_str) {
25 result.rename_all = Some(convention);
26 }
27 }
28 }
29 }
30
31 result
32 }
33
34 pub fn parse_field_serde_attrs(&self, attrs: &[Attribute]) -> SerdeFieldAttributes {
36 let mut result = SerdeFieldAttributes {
37 rename: None,
38 skip: false,
39 };
40
41 for attr in attrs {
42 if attr.path().is_ident("serde") {
43 if let Ok(tokens) = syn::parse2::<syn::MetaList>(attr.meta.to_token_stream()) {
44 let tokens_str = tokens.tokens.to_string();
45
46 if tokens_str.contains("skip") && !tokens_str.contains("skip_serializing") {
48 result.skip = true;
49 }
50
51 if let Some(rename) = self.parse_rename(&tokens_str) {
53 result.rename = Some(rename);
54 }
55 }
56 }
57 }
58
59 result
60 }
61
62 fn parse_rename_all(&self, tokens: &str) -> Option<RenameRule> {
65 if let Some(start) = tokens.find("rename_all") {
66 if let Some(eq_pos) = tokens[start..].find('=') {
67 let after_eq = &tokens[start + eq_pos + 1..].trim_start();
68
69 if let Some(quote_start) = after_eq.find('"') {
71 if let Some(quote_end) = after_eq[quote_start + 1..].find('"') {
72 let value = &after_eq[quote_start + 1..quote_start + 1 + quote_end];
73
74 return RenameRule::from_rename_all_str(value).ok();
75 }
76 }
77 }
78 }
79 None
80 }
81
82 fn parse_rename(&self, tokens: &str) -> Option<String> {
84 let mut search_start = 0;
86 while let Some(pos) = tokens[search_start..].find("rename") {
87 let abs_pos = search_start + pos;
88
89 let after_rename = &tokens[abs_pos + 6..];
91 if after_rename.trim_start().starts_with("_all") {
92 search_start = abs_pos + 10; continue;
95 }
96
97 if let Some(eq_pos) = after_rename.find('=') {
99 let after_eq = &after_rename[eq_pos + 1..].trim_start();
100
101 if let Some(quote_start) = after_eq.find('"') {
103 if let Some(quote_end) = after_eq[quote_start + 1..].find('"') {
104 let value = &after_eq[quote_start + 1..quote_start + 1 + quote_end];
105 return Some(value.to_string());
106 }
107 }
108 }
109
110 break;
111 }
112 None
113 }
114}
115
116impl Default for SerdeParser {
117 fn default() -> Self {
118 Self::new()
119 }
120}
121
122#[derive(Debug, Default, Clone)]
124pub struct SerdeStructAttributes {
125 pub rename_all: Option<RenameRule>,
126}
127
128#[derive(Debug, Default, Clone)]
130pub struct SerdeFieldAttributes {
131 pub rename: Option<String>,
132 pub skip: bool,
133}
134#[cfg(test)]
135mod tests {
136 use super::*;
137 use syn::parse_quote;
138
139 #[test]
140 fn test_parse_rename_all_camel_case() {
141 let parser = SerdeParser::new();
142 let result = parser.parse_rename_all(r#"rename_all = "camelCase""#);
143
144 assert!(result.is_some());
145 assert!(matches!(result.unwrap(), RenameRule::CamelCase));
146 }
147
148 #[test]
149 fn test_parse_rename_all_snake_case() {
150 let parser = SerdeParser::new();
151 let result = parser.parse_rename_all(r#"rename_all = "snake_case""#);
152
153 assert!(result.is_some());
154 assert!(matches!(result.unwrap(), RenameRule::SnakeCase));
155 }
156
157 #[test]
158 fn test_parse_rename_all_pascal_case() {
159 let parser = SerdeParser::new();
160 let result = parser.parse_rename_all(r#"rename_all = "PascalCase""#);
161
162 assert!(result.is_some());
163 assert!(matches!(result.unwrap(), RenameRule::PascalCase));
164 }
165
166 #[test]
167 fn test_parse_rename_all_screaming_snake_case() {
168 let parser = SerdeParser::new();
169 let result = parser.parse_rename_all(r#"rename_all = "SCREAMING_SNAKE_CASE""#);
170
171 assert!(result.is_some());
172 assert!(matches!(result.unwrap(), RenameRule::ScreamingSnakeCase));
173 }
174
175 #[test]
176 fn test_parse_rename_all_kebab_case() {
177 let parser = SerdeParser::new();
178 let result = parser.parse_rename_all(r#"rename_all = "kebab-case""#);
179
180 assert!(result.is_some());
181 assert!(matches!(result.unwrap(), RenameRule::KebabCase));
182 }
183
184 #[test]
185 fn test_parse_rename_all_not_present() {
186 let parser = SerdeParser::new();
187 let result = parser.parse_rename_all(r#"skip_serializing_if = "Option::is_none""#);
188
189 assert!(result.is_none());
190 }
191
192 #[test]
193 fn test_parse_rename() {
194 let parser = SerdeParser::new();
195
196 let result = parser.parse_rename(r#"rename = "customName""#);
197 assert_eq!(result, Some("customName".to_string()));
198
199 let result = parser.parse_rename(r#"rename = "id""#);
200 assert_eq!(result, Some("id".to_string()));
201 }
202
203 #[test]
204 fn test_parse_rename_not_rename_all() {
205 let parser = SerdeParser::new();
206
207 let result = parser.parse_rename(r#"rename_all = "camelCase""#);
209 assert!(result.is_none());
210 }
211
212 #[test]
213 fn test_parse_rename_with_rename_all_present() {
214 let parser = SerdeParser::new();
215
216 let result = parser.parse_rename(r#"rename_all = "camelCase", rename = "id""#);
218 assert_eq!(result, Some("id".to_string()));
219 }
220
221 #[test]
222 fn test_parse_struct_serde_attrs_with_rename_all() {
223 let parser = SerdeParser::new();
224 let attrs: Vec<Attribute> = vec![parse_quote!(#[serde(rename_all = "camelCase")])];
225
226 let result = parser.parse_struct_serde_attrs(&attrs);
227 assert!(result.rename_all.is_some());
228 assert!(matches!(result.rename_all.unwrap(), RenameRule::CamelCase));
229 }
230
231 #[test]
232 fn test_parse_struct_serde_attrs_no_serde() {
233 let parser = SerdeParser::new();
234 let attrs: Vec<Attribute> = vec![parse_quote!(#[derive(Debug)])];
235
236 let result = parser.parse_struct_serde_attrs(&attrs);
237 assert!(result.rename_all.is_none());
238 }
239
240 #[test]
241 fn test_parse_field_serde_attrs_with_rename() {
242 let parser = SerdeParser::new();
243 let attrs: Vec<Attribute> = vec![parse_quote!(#[serde(rename = "customName")])];
244
245 let result = parser.parse_field_serde_attrs(&attrs);
246 assert_eq!(result.rename, Some("customName".to_string()));
247 assert!(!result.skip);
248 }
249
250 #[test]
251 fn test_parse_field_serde_attrs_with_skip() {
252 let parser = SerdeParser::new();
253 let attrs: Vec<Attribute> = vec![parse_quote!(#[serde(skip)])];
254
255 let result = parser.parse_field_serde_attrs(&attrs);
256 assert!(result.skip);
257 assert!(result.rename.is_none());
258 }
259
260 #[test]
261 fn test_parse_field_serde_attrs_skip_serializing_not_skip() {
262 let parser = SerdeParser::new();
263 let attrs: Vec<Attribute> = vec![parse_quote!(#[serde(skip_serializing)])];
264
265 let result = parser.parse_field_serde_attrs(&attrs);
266 assert!(!result.skip);
268 }
269
270 #[test]
271 fn test_parse_field_serde_attrs_multiple() {
272 let parser = SerdeParser::new();
273 let attrs: Vec<Attribute> = vec![
274 parse_quote!(#[serde(rename = "id")]),
275 parse_quote!(#[derive(Debug)]),
276 ];
277
278 let result = parser.parse_field_serde_attrs(&attrs);
279 assert_eq!(result.rename, Some("id".to_string()));
280 }
281
282 #[test]
283 fn test_parse_field_serde_attrs_no_serde() {
284 let parser = SerdeParser::new();
285 let attrs: Vec<Attribute> = vec![parse_quote!(#[derive(Debug)])];
286
287 let result = parser.parse_field_serde_attrs(&attrs);
288 assert!(result.rename.is_none());
289 assert!(!result.skip);
290 }
291
292 #[test]
293 fn test_default_impl() {
294 let parser = SerdeParser;
295 let result = parser.parse_rename(r#"rename = "test""#);
296 assert_eq!(result, Some("test".to_string()));
297 }
298}