Skip to main content

gtk3_macros/
composite_template_derive.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use proc_macro2::Span;
4use proc_macro2::TokenStream;
5use quote::quote;
6use syn::Data;
7
8use std::string::ToString;
9
10use crate::attribute_parser::*;
11use crate::util::*;
12
13fn gen_set_template(source: TemplateSource) -> TokenStream {
14    match source {
15        TemplateSource::File(file) => quote! {
16            let t = include_bytes!(#file);
17            klass.set_template(t);
18        },
19        TemplateSource::Resource(resource) => quote! {
20            klass.set_template_from_resource(&#resource);
21        },
22        TemplateSource::String(template) => quote! {
23            klass.set_template(&#template);
24        },
25    }
26}
27
28fn gen_template_child_bindings(fields: &syn::Fields) -> TokenStream {
29    let crate_ident = crate_ident_new();
30    let attributed_fields = match parse_fields(fields) {
31        Ok(fields) => fields,
32        Err(err) => return err.into_compile_error(),
33    };
34
35    let recurse = attributed_fields.iter().map(|field| match field.attr.ty {
36        FieldAttributeType::TemplateChild => {
37            let mut value_id = &field.ident.to_string();
38            let ident = &field.ident;
39            field.attr.args.iter().for_each(|arg| match arg {
40                FieldAttributeArg::Id(value) => {
41                    value_id = value;
42                }
43            });
44
45            quote! {
46                klass.bind_template_child_with_offset(
47                    &#value_id,
48                    #crate_ident::offset_of!(Self => #ident),
49                );
50            }
51        }
52    });
53
54    quote! {
55        #(#recurse)*
56    }
57}
58
59pub fn impl_composite_template(input: &syn::DeriveInput) -> TokenStream {
60    let name = &input.ident;
61    let crate_ident = crate_ident_new();
62
63    let source = match parse_template_source(input) {
64        Ok(v) => v,
65        Err(e) => return e.into_compile_error_with_message(
66            "derive(CompositeTemplate) requires #[template(...)] to specify 'file', 'resource', or 'string'",
67        ),
68    };
69
70    let set_template = gen_set_template(source);
71
72    let fields = match input.data {
73        Data::Struct(ref s) => &s.fields,
74        _ => {
75            return syn::Error::new(
76                Span::call_site(),
77                "derive(CompositeTemplate) only supports structs",
78            )
79            .into_compile_error();
80        }
81    };
82
83    let template_children = gen_template_child_bindings(fields);
84
85    quote! {
86        impl #crate_ident::subclass::widget::CompositeTemplate for #name {
87            fn bind_template(klass: &mut Self::Class) {
88                #set_template
89
90                unsafe {
91                    #template_children
92                }
93            }
94        }
95    }
96}