1 use crate::attrib::{AttrError, get_field_attr_once};
2 use proc_macro::TokenStream;
3 use quote::{format_ident, quote};
4 use std::fmt;
5 use syn::{Attribute, DataStruct, Field, Fields, Ident, LitBool, Type};
6
7 // accepted sub keys for the "vertex" key
8 const KNOWN_SUBKEYS: &[&str] = &["sem", "instanced", "normalized"];
9
10 #[derive(Debug)]
11 pub(crate) enum StructImplError {
12 SemanticsError(AttrError),
13 FieldError(AttrError),
14 UnsupportedUnit,
15 }
16
17 impl fmt::Display for StructImplError {
fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>18 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
19 match *self {
20 StructImplError::SemanticsError(ref e) =>
21 write!(f, "error with semantics type; {}", e),
22 StructImplError::FieldError(ref e) =>
23 write!(f, "error with vertex attribute field; {}", e),
24 StructImplError::UnsupportedUnit => f.write_str("unsupported unit struct"),
25 }
26 }
27 }
28
29 /// Generate the Vertex impl for a struct.
generate_vertex_impl<'a, A>( ident: Ident, attrs: A, struct_: DataStruct ) -> Result<TokenStream, StructImplError> where A: Iterator<Item = &'a Attribute> + Clone30 pub(crate) fn generate_vertex_impl<'a, A>(
31 ident: Ident,
32 attrs: A,
33 struct_: DataStruct
34 ) -> Result<TokenStream, StructImplError>
35 where A: Iterator<Item = &'a Attribute> + Clone {
36 // search the semantics name
37 let sem_type: Type = get_field_attr_once(
38 &ident,
39 attrs.clone(),
40 "vertex",
41 "sem",
42 KNOWN_SUBKEYS
43 ).map_err(StructImplError::SemanticsError)?;
44
45 let instancing = get_instancing(&ident, attrs.clone())?;
46
47 match struct_.fields {
48 Fields::Unnamed(unnamed_fields) => {
49 let mut indexed_vertex_attrib_descs = Vec::new();
50 let mut fields_types = Vec::new();
51
52 for (i, field) in unnamed_fields.unnamed.into_iter().enumerate() {
53 let field_ident = format_ident!("field_{}", i);
54
55 process_field(
56 &field,
57 field_ident,
58 &sem_type,
59 &instancing,
60 &mut indexed_vertex_attrib_descs,
61 &mut fields_types,
62 None
63 )?;
64 }
65
66 let output = process_struct(ident, indexed_vertex_attrib_descs, Vec::new(), fields_types);
67 Ok(output.into())
68 }
69
70 Fields::Named(named_fields) => {
71 let mut indexed_vertex_attrib_descs = Vec::new();
72 let mut fields_types = Vec::new();
73 let mut fields_names = Vec::new();
74
75 for field in named_fields.named {
76 let field_ident = field.ident.clone().unwrap();
77
78 process_field(
79 &field,
80 field_ident,
81 &sem_type,
82 &instancing,
83 &mut indexed_vertex_attrib_descs,
84 &mut fields_types,
85 &mut fields_names
86 )?;
87 }
88
89 let output = process_struct(ident, indexed_vertex_attrib_descs, fields_names, fields_types);
90 Ok(output.into())
91 }
92
93 Fields::Unit => Err(StructImplError::UnsupportedUnit)
94 }
95 }
96
process_field<'a, FN>( field: &Field, ident: Ident, sem_type: &Type, instancing: &proc_macro2::TokenStream, indexed_vertex_attrib_descs: &mut Vec<proc_macro2::TokenStream>, fields_types: &mut Vec<Type>, fields_names: FN, ) -> Result<(), StructImplError> where FN: Into<Option<&'a mut Vec<Ident>>>97 fn process_field<'a, FN>(
98 field: &Field,
99 ident: Ident,
100 sem_type: &Type,
101 instancing: &proc_macro2::TokenStream,
102 indexed_vertex_attrib_descs: &mut Vec<proc_macro2::TokenStream>,
103 fields_types: &mut Vec<Type>,
104 fields_names: FN,
105 ) -> Result<(), StructImplError>
106 where FN: Into<Option<&'a mut Vec<Ident>>> {
107 // search for the normalized argument; if not there, we don’t normalize anything
108 let normalized = get_field_attr_once(
109 &ident,
110 &field.attrs,
111 "vertex",
112 "normalized",
113 KNOWN_SUBKEYS
114 ).map(|b: LitBool| b.value)
115 .or_else(|e| match e {
116 AttrError::CannotFindAttribute(..) => Ok(false),
117 _ => Err(e)
118 })
119 .map_err(StructImplError::FieldError)?;
120
121 let field_ty = &field.ty;
122 let vertex_attrib_desc = if normalized {
123 quote!{ (<#field_ty as luminance::vertex::VertexAttrib>::VERTEX_ATTRIB_DESC).normalize() }
124 } else {
125 quote!{ <#field_ty as luminance::vertex::VertexAttrib>::VERTEX_ATTRIB_DESC }
126 };
127
128 let indexed_vertex_attrib_desc_q = quote!{
129 luminance::vertex::VertexBufferDesc::new::<#sem_type>(
130 <#field_ty as luminance::vertex::HasSemantics>::SEMANTICS,
131 #instancing,
132 #vertex_attrib_desc,
133 )
134 };
135
136 indexed_vertex_attrib_descs.push(indexed_vertex_attrib_desc_q);
137 fields_types.push(field_ty.clone());
138
139 if let Some(fields_names) = fields_names.into() {
140 fields_names.push(ident);
141 }
142
143 Ok(())
144 }
145
146 /// Process the output struct.
147 ///
148 /// If fields_names is empty, it is assumed to be a struct-tuple.
process_struct( struct_name: Ident, indexed_vertex_attrib_descs: Vec<proc_macro2::TokenStream>, fields_names: Vec<Ident>, fields_types: Vec<Type> ) -> proc_macro2::TokenStream149 fn process_struct(
150 struct_name: Ident,
151 indexed_vertex_attrib_descs: Vec<proc_macro2::TokenStream>,
152 fields_names: Vec<Ident>,
153 fields_types: Vec<Type>
154 ) -> proc_macro2::TokenStream {
155 let fn_new = if fields_names.is_empty() {
156 // struct tuple
157 let i: Vec<_> = (0 .. fields_types.len()).map(|i| format_ident!("field_{}", i)).collect();
158
159 quote! {
160 impl #struct_name {
161 pub const fn new(#(#i : #fields_types),*) -> Self {
162 #struct_name ( #(#i),* )
163 }
164 }
165 }
166 } else {
167 quote! {
168 impl #struct_name {
169 pub const fn new(#(#fields_names : #fields_types),*) -> Self {
170 #struct_name { #(#fields_names),* }
171 }
172 }
173 }
174 };
175
176 quote! {
177 // Vertex impl
178 unsafe impl luminance::vertex::Vertex for #struct_name {
179 fn vertex_desc() -> luminance::vertex::VertexDesc {
180 vec![#(#indexed_vertex_attrib_descs),*]
181 }
182 }
183
184 // helper function for the generate type
185 #fn_new
186 }
187 }
188
get_instancing<'a, A>( ident: &Ident, attrs: A ) -> Result<proc_macro2::TokenStream, StructImplError> where A: IntoIterator<Item = &'a Attribute>189 fn get_instancing<'a, A>(
190 ident: &Ident,
191 attrs: A
192 ) -> Result<proc_macro2::TokenStream, StructImplError>
193 where A: IntoIterator<Item = &'a Attribute> {
194 // search for the instancing argument; if not there, we don’t use vertex instancing
195 get_field_attr_once(
196 &ident,
197 attrs,
198 "vertex",
199 "instanced",
200 KNOWN_SUBKEYS
201 ).map(|b: LitBool| {
202 if b.value {
203 quote! { luminance::vertex::VertexInstancing::On }
204 } else {
205 quote! { luminance::vertex::VertexInstancing::Off }
206 }
207 }).or_else(|e| match e {
208 AttrError::CannotFindAttribute(..) => {
209 Ok(quote! { luminance::vertex::VertexInstancing::Off })
210 }
211
212 _ => Err(e)
213 }).map_err(StructImplError::FieldError)
214 }
215