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