gff_derive/
lib.rs

1use proc_macro::TokenStream;
2use syn::{parse_macro_input, DeriveInput};
3
4extern crate proc_macro;
5#[macro_use]
6extern crate quote;
7
8/* {{{ GFF Structs */
9
10struct GFFStructId(syn::LitInt);
11
12impl syn::parse::Parse for GFFStructId {
13    fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
14        let content;
15        syn::parenthesized!(content in input);
16        let st_id = content.parse()?;
17        Ok(GFFStructId(st_id))
18    }
19}
20
21#[proc_macro_derive(GFFStruct, attributes(GFFStructId))]
22pub fn derive_gff_struct(input: TokenStream) -> TokenStream {
23    // Parse the input tokens into a syntax tree
24    let input = parse_macro_input!(input as DeriveInput);
25
26    // parse attribute GFFStructId
27    let attribute = input.attrs.iter().find(
28        |a| a.path.segments.len() == 1 && a.path.segments[0].ident == "GFFStructId"
29    ).expect("GFFStructId attribute required for deriving GFFStruct");
30    let parameters: GFFStructId = syn::parse2(attribute.tokens.clone())
31        .expect("Invalid GFFStructId attribute!");
32
33    let struct_name = &input.ident;
34    let input = input.data;
35    let struct_id = parameters.0;
36
37    match input {
38        syn::Data::Enum(_) => { panic!("Expected struct, got enum"); }
39        syn::Data::Union(_) => { panic!("Expected struct, got union"); }
40        syn::Data::Struct(data_struct) => {
41            match data_struct.fields {
42                syn::Fields::Unnamed(_) => { panic!("Expected named fields, got unnamed"); }
43                syn::Fields::Unit => { panic!("Expected named fields, got unit"); }
44                syn::Fields::Named(named_fields) => {
45                    let fields : Vec<&syn::Ident> = named_fields.named.iter().map(|field| {
46                        field.ident.as_ref().unwrap()
47                    }).collect();
48                    let keys : Vec<String> = fields.iter().map(|ident| {
49                        ident.to_string()
50                    }).collect();
51
52                    // Build the output, possibly using quasi-quotation
53                    let expanded = quote! {
54                        /* deserializing from GffStruct to custom structure. */
55                        impl std::convert::TryFrom<&GffFieldValue> for #struct_name {
56                            type Error = &'static str;
57
58                            fn try_from(value: &GffFieldValue) -> Result<Self, self::Error> {
59                                match value {
60                                    ::gff::common::GffFieldValue::Struct(s) => 
61                                        ::gff::common::Deserialize::deserialize(s),
62                                    _ => Err("Expected Struct"),
63                                }
64                            }
65                        }
66                        impl ::gff::common::Deserialize for #struct_name {
67                            fn deserialize(s: &::gff::common::GffStruct)
68                                -> Result<Self, &'static str> where Self: std::marker::Sized {
69                                Ok(#struct_name {
70                                    #(
71                                        #fields : std::convert::TryFrom::try_from(
72                                            s.fields.get(#keys)
73                                                    .ok_or("key not found")?
74                                        )?
75                                    ),*
76                                })
77                            }
78                        }
79
80                        /* serializing from custom structure to GffStruct. */
81                        impl ::gff::common::Serialize for #struct_name {
82                            fn serialize(&self) -> Result<GffStruct, &'static str> {
83                                Ok(GffStruct {
84                                    st_type: #struct_id,
85                                    fields: HashMap::from([
86                                        #(
87                                            (#keys.to_string(), (&self.#fields).try_into()?)
88                                        ),*
89                                    ])
90                                })
91                            }
92                        }
93                        impl std::convert::TryInto<GffFieldValue> for &#struct_name {
94                            type Error = &'static str;
95
96                            fn try_into(self) -> Result<GffFieldValue, self::Error> {
97                                Ok(GffFieldValue::Struct(::gff::common::Serialize::serialize(self)?))
98                            }
99                        }
100                    };
101
102                    // Hand the output tokens back to the compiler
103                    TokenStream::from(expanded)
104                }
105            }
106        }
107    }
108}
109
110#[proc_macro_derive(GFFStructPack, attributes(GFFStructId))]
111pub fn derive_gff_struct_pack(input: TokenStream) -> TokenStream {
112    // Parse the input tokens into a syntax tree
113    let input = parse_macro_input!(input as DeriveInput);
114
115    // parse attribute GFFStructId
116    let attribute = input.attrs.iter().find(
117        |a| a.path.segments.len() == 1 && a.path.segments[0].ident == "GFFStructId"
118    ).expect("GFFStructId attribute required for deriving GFFStruct");
119    let parameters: GFFStructId = syn::parse2(attribute.tokens.clone())
120        .expect("Invalid GFFStructId attribute!");
121
122    let struct_name = &input.ident;
123    let input = input.data;
124    let struct_id = parameters.0;
125
126    match input {
127        syn::Data::Enum(_) => { panic!("Expected struct, got enum"); }
128        syn::Data::Union(_) => { panic!("Expected struct, got union"); }
129        syn::Data::Struct(data_struct) => {
130            let field_count = data_struct.fields.len();
131            match data_struct.fields {
132                syn::Fields::Unnamed(_) => { panic!("Expected named fields, got unnamed"); }
133                syn::Fields::Unit => { panic!("Expected named fields, got unit"); }
134                syn::Fields::Named(named_fields) => {
135                    let fields : Vec<&syn::Ident> = named_fields.named.iter().map(|field| {
136                        field.ident.as_ref().unwrap()
137                    }).collect();
138                    let keys : Vec<String> = fields.iter().map(|ident| {
139                        ident.to_string()
140                    }).collect();
141
142                    let pack_field_idx = match field_count {
143                        0..=1 => quote! {
144                            packer.data.structs.extend_from_slice(
145                                &(packer.data.header.fields.1 as u32).to_le_bytes()
146                            );
147                        },
148                        _ => quote! {
149                            packer.data.structs.extend_from_slice(
150                                &(packer.data.header.field_indices.1 as u32).to_le_bytes()
151                            );
152                        },
153                    };
154
155                    // Build the output, possibly using quasi-quotation
156                    let expanded = quote! {
157                        impl <'a, W: std::io::Write> gff::packer::PackField<'a, W> for #struct_name {
158                            fn pack_field(&'a self, label: String, packer: &mut gff::packer::Packer<W>,
159                                structs: &mut Vec<&'a dyn gff::packer::PackStruct<W>>, st_idx: &mut u32)
160                                -> ()
161                            {
162                                let label_idx = packer.pack_label(&label).unwrap();
163
164                                *st_idx += 1;
165                                packer.pack_val_4(14, label_idx, &(*st_idx).to_le_bytes());
166                                structs.push(self);
167                            }
168                        }
169
170                        /* serializing from custom structure to GffStruct. */
171                        impl<'a, W: std::io::Write> ::gff::packer::PackStruct<'a, W> for #struct_name {
172                            fn pack(&'a self, packer: &mut ::gff::packer::Packer<W>,
173                                structs: &mut Vec<&'a dyn ::gff::packer::PackStruct<W>>,
174                                st_idx: &mut u32)
175                                -> ()
176                            {
177                                // pack struct id
178                                packer.data.structs.extend_from_slice(
179                                    &(#struct_id as u32).to_le_bytes()
180                                );
181                                // pack field indices / field id
182                                #pack_field_idx
183                                // pack field count
184                                packer.data.structs.extend_from_slice(
185                                    &(#field_count as u32).to_le_bytes()
186                                );
187                                // pack fields
188                                #(
189                                    gff::packer::PackField::pack_field(
190                                        &self.#fields, #keys.to_string(),
191                                        packer, structs, st_idx
192                                    );
193                                )*;
194                                // Ok()
195                            }
196                        }
197                    };
198
199                    // Hand the output tokens back to the compiler
200                    TokenStream::from(expanded)
201                }
202            }
203        }
204    }
205}
206
207/* }}} */