Skip to main content

serde_infer/
lib.rs

1use core::fmt;
2
3use heck::*;
4use proc_macro::TokenStream;
5use quote::ToTokens;
6use syn::{parse, parse_macro_input, parse_quote, Attribute, ExprAssign, ItemStruct, LitStr};
7
8#[proc_macro_attribute]
9pub fn serde_infer(opt: TokenStream, i: TokenStream) -> TokenStream {
10    let mut data = parse_macro_input!(i as ItemStruct);
11    let case = parse::<ExprAssign>(opt)
12        .ok()
13        .map(|f| parse::<LitStr>(f.right.to_token_stream().into()).ok())
14        .flatten();
15
16    data.fields.iter_mut().for_each(|f| {
17        f.attrs
18            .extend_from_slice(&cases(f.ident.clone().unwrap().to_string()));
19
20        case.clone().inspect(|c| {
21            f.attrs.extend_from_slice({
22                let out = outgoing(c.value(), f.ident.clone().unwrap().to_string());
23                &[parse_quote!(#[serde(rename = #out)])]
24            })
25        });
26    });
27
28    data.to_token_stream().into()
29}
30
31fn outgoing(case: String, field: String) -> String {
32    match case.as_str() {
33        "kebab" => AsKebabCase(&field).to_string(),
34        "lower_camel" => AsLowerCamelCase(&field).to_string(),
35        "pascal" => AsPascalCase(&field).to_string(),
36        "shouty_kebab" => AsShoutyKebabCase(&field).to_string(),
37        "shouty_snake" => AsShoutySnakeCase(&field).to_string(),
38        "snake" => AsSnakeCase(&field).to_string(),
39        "title" => AsTitleCase(&field).to_string(),
40        "train" => AsTrainCase(&field).to_string(),
41        "upper_camel" => AsUpperCamelCase(&field).to_string(),
42        "uppercase" | "upper" => field.to_uppercase(),
43        "lowercase" | "lower" => field.to_lowercase(),
44        _ => panic!("Invalid casing: {}", case),
45    }
46}
47
48fn cases(field: String) -> Vec<Attribute> {
49    vec![
50        AsKebabCase(&field).to_string(),
51        AsLowerCamelCase(&field).to_string(),
52        AsPascalCase(&field).to_string(),
53        AsShoutyKebabCase(&field).to_string(),
54        AsShoutySnakeCase(&field).to_string(),
55        AsShoutySnekCase(&field).to_string(),
56        AsSnakeCase(&field).to_string(),
57        AsSnekCase(&field).to_string(),
58        AsTitleCase(&field).to_string(),
59        AsTrainCase(&field).to_string(),
60        AsUpperCamelCase(&field).to_string(),
61        field.to_uppercase(),
62        field.to_lowercase(),
63    ]
64    .iter()
65    .map(|t| {
66        let field = t.to_string();
67        parse_quote!(#[serde(alias = #field)])
68    })
69    .collect::<Vec<Attribute>>()
70}