1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
use proc_macro2::Span;
use quote::quote;
use syn::{
    parse_quote, punctuated::Punctuated, token::Comma, Attribute, Expr, FieldValue, Fields, LitInt,
};

pub const CANONIZED_POSTFIX: &str = "Canon";

pub fn canonize_fields(fields: &Fields, canonize: bool) -> proc_macro2::TokenStream {
    match &fields {
        Fields::Named(fields) => {
            let mut members: Punctuated<FieldValue, Comma> = Punctuated::new();

            for field in fields.named.iter() {
                let name = field.ident.as_ref().unwrap(); // It's a named field.
                let value = if canonize {
                    parse_quote!(#name: fadroma::prelude::Canonize::canonize(self.#name, api)?)
                } else {
                    parse_quote!(#name: fadroma::prelude::Humanize::humanize(self.#name, api)?)
                };

                members.push(value);
            }

            quote!({ #members })
        }
        Fields::Unnamed(fields) => {
            let mut members: Punctuated<Expr, Comma> = Punctuated::new();

            for i in 0..fields.unnamed.len() {
                let lit = LitInt::new(&i.to_string(), Span::call_site());

                let value = if canonize {
                    parse_quote!(fadroma::prelude::Canonize::canonize(self.#lit, api)?)
                } else {
                    parse_quote!(fadroma::prelude::Humanize::humanize(self.#lit, api)?)
                };

                members.push(value);
            }

            quote!((#members))
        }
        Fields::Unit => proc_macro2::TokenStream::new(),
    }
}

pub fn add_serde_derive(attrs: &mut Vec<Attribute>) {
    attrs.clear();
    attrs.push(parse_quote!(#[derive(serde::Serialize)]));
    attrs.push(parse_quote!(#[derive(serde::Deserialize)]));
    attrs.push(parse_quote!(#[derive(fadroma::bin_serde::FadromaSerialize)]));
    attrs.push(parse_quote!(#[derive(fadroma::bin_serde::FadromaDeserialize)]));
}

pub fn transform_fields(fields: &mut Fields) -> syn::Result<()> {
    match fields {
        Fields::Named(fields) => {
            for mut field in fields.named.iter_mut() {
                let ty = &field.ty;
                field.ty = parse_quote!(<#ty as fadroma::prelude::Canonize>::Output);
            }
        }
        Fields::Unnamed(fields) => {
            for mut field in fields.unnamed.iter_mut() {
                let ty = &field.ty;
                field.ty = parse_quote!(<#ty as fadroma::prelude::Canonize>::Output);
            }
        }
        Fields::Unit => { }
    }

    Ok(())
}