1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use quote::{format_ident, quote};
5use syn::{self, parse_macro_input, Data, DeriveInput, Field, Fields, Meta, NestedMeta};
6
7fn has_attr(field: &Field, i: &str) -> bool {
8 field.attrs.iter().any(|attr| {
9 if let Some(attr_ident) = attr.path.get_ident() {
10 attr_ident == i
11 } else {
12 false
13 }
14 })
15}
16
17#[proc_macro_derive(Shader, attributes(uniform))]
18pub fn derive_shader(item: TokenStream) -> TokenStream {
19 let input = parse_macro_input!(item as DeriveInput);
20
21 let ident = input.ident;
22
23 let fields = match input.data {
24 Data::Struct(s) => match s.fields {
25 Fields::Named(fields) => fields
26 .named
27 .iter()
28 .filter(|field| has_attr(field, "uniform"))
29 .map(|field| {
30 let field_ident = field.ident.as_ref().unwrap();
31 let field_ty = &field.ty;
32
33 quote! {
34 impl ::solstice::shader::UniformGetter<#field_ty> for #ident {
35 fn get_uniform(&self) -> &#field_ty {
36 &self.#field_ident
37 }
38 }
39
40 impl ::solstice::shader::UniformGetterMut<#field_ty> for #ident {
41 fn get_uniform_mut(&mut self) -> &mut #field_ty {
42 &mut self.#field_ident
43 }
44 }
45 }
46 })
47 .collect::<Vec<_>>(),
48 _ => panic!("only named fields are supported"),
49 },
50 _ => panic!("only structs are supported"),
51 };
52
53 TokenStream::from(quote! {
54 #(#fields)*
55 impl ::solstice::shader::BasicUniformSetter for #ident {}
56 })
57}
58
59#[proc_macro_derive(Uniform, attributes(location))]
60pub fn derive_uniform(item: TokenStream) -> TokenStream {
61 let input = parse_macro_input!(item as DeriveInput);
62
63 let ident = input.ident;
64
65 let fields = match input.data {
66 Data::Struct(s) => match s.fields {
67 Fields::Named(fields) => fields
68 .named
69 .iter()
70 .filter(|field| has_attr(field, "location"))
71 .map(|field| {
72 let field_ident = field.ident.as_ref().unwrap();
73 quote! {
74 impl ::solstice::shader::UniformTrait for #ident {
75 type Value = [f32; 16];
76 const NAME: &'static str = "#field_ident";
77
78 fn get_location(&self) -> Option<&::solstice::shader::UniformLocation> {
79 self.#field_ident.as_ref()
80 }
81 }
82 }
83 })
84 .collect::<Vec<_>>(),
85 _ => panic!("only named fields are supported"),
86 },
87 _ => panic!("only structs are supported"),
88 };
89
90 TokenStream::from(quote! {
91 #(#fields)*
92 })
93}
94
95#[proc_macro_derive(Vertex)]
96pub fn derive_vertex(item: TokenStream) -> TokenStream {
97 let input = parse_macro_input!(item as DeriveInput);
98
99 assert!(
100 input.attrs.iter().any(|attr| {
101 if let Some(ident) = attr.path.get_ident() {
102 if ident == "repr" {
103 if let Ok(syn::Meta::List(ref meta_list)) = attr.parse_meta() {
104 let reprs = meta_list
105 .nested
106 .iter()
107 .filter_map(|nested| match nested {
108 NestedMeta::Meta(Meta::Path(path)) => path.get_ident(),
109 _ => None,
110 })
111 .collect::<Vec<_>>();
112 return ["C"]
113 .iter()
114 .all(|repr| reprs.iter().find(|&r| r == repr).is_some());
115 }
116 }
117 }
118 false
119 }),
120 "Vertex structs must be `#[repr(C)]`"
121 );
122
123 match input.data {
124 Data::Struct(s) => match s.fields {
125 Fields::Named(fields) => {
126 let field_types = fields.named.iter().scan(vec![], |state, field| {
127 state.push(&field.ty);
128 Some(state.clone())
129 });
130
131 let vertex_formats = field_types.zip(fields.named.iter()).map(|(mut types, ident)| {
132 let this_type = types.pop().unwrap();
133 let name = format!("{:}", ident.ident.as_ref().unwrap());
134 let offset = if types.is_empty() {
135 quote! {
136 0usize
137 }
138 } else {
139 quote! {
140 #(::std::mem::size_of::<#types>())+*
141 }
142 };
143
144 quote! {
145 ::solstice::vertex::VertexFormat {
146 name: #name,
147 offset: #offset,
148 atype: <#this_type as ::solstice::vertex::VertexAttributeType>::A_TYPE,
149 normalize: false,
150 }
151 }
152 });
153 let ident = format_ident!("{}", input.ident);
154 TokenStream::from(quote! {
155 impl ::solstice::vertex::Vertex for #ident {
156 fn build_bindings() -> &'static [::solstice::vertex::VertexFormat] {
157 &[
158 #(#vertex_formats),*
159 ]
160 }
161 }
162 })
163 }
164 Fields::Unnamed(_) | Fields::Unit => panic!("only named fields are supported"),
165 },
166 Data::Enum(_) | Data::Union(_) => panic!("only structs are supported"),
167 }
168}