1extern crate proc_macro;
2use std::collections::HashSet;
3
4use layout::{get_struct_member_layout, StructMemberLayout};
6use proc_macro::TokenStream;
7use quote::quote;
8use syn::DeriveInput;
9use syn::{
10 parse_macro_input, Attribute, Data, Error, Field, Fields, GenericArgument, Ident, Lit,
11 NestedMeta, PathArguments, Result, Type, TypePath,
12};
13
14mod layout;
15
16enum PasturePrimitiveType {
17 U8,
18 I8,
19 U16,
20 I16,
21 U32,
22 I32,
23 U64,
24 I64,
25 F32,
26 F64,
27 Vec3u8,
28 Vec3u16,
29 Vec3f32,
30 Vec3f64,
31 Vec3i32,
32 Vec4u8,
33}
34
35impl PasturePrimitiveType {
36 fn min_alignment(&self) -> u64 {
37 match self {
38 PasturePrimitiveType::U8 => 1,
39 PasturePrimitiveType::I8 => 1,
40 PasturePrimitiveType::U16 => 2,
41 PasturePrimitiveType::I16 => 2,
42 PasturePrimitiveType::U32 => 4,
43 PasturePrimitiveType::I32 => 4,
44 PasturePrimitiveType::U64 => 8,
45 PasturePrimitiveType::I64 => 8,
46 PasturePrimitiveType::F32 => 4,
47 PasturePrimitiveType::F64 => 8,
48 PasturePrimitiveType::Vec3u8 => 1,
49 PasturePrimitiveType::Vec3u16 => 2,
50 PasturePrimitiveType::Vec3f32 => 4,
51 PasturePrimitiveType::Vec3f64 => 8,
52 PasturePrimitiveType::Vec3i32 => 4,
53 &PasturePrimitiveType::Vec4u8 => 1,
54 }
55 }
56
57 fn size(&self) -> u64 {
58 match self {
59 PasturePrimitiveType::U8 => 1,
60 PasturePrimitiveType::I8 => 1,
61 PasturePrimitiveType::U16 => 2,
62 PasturePrimitiveType::I16 => 2,
63 PasturePrimitiveType::U32 => 4,
64 PasturePrimitiveType::I32 => 4,
65 PasturePrimitiveType::U64 => 8,
66 PasturePrimitiveType::I64 => 8,
67 PasturePrimitiveType::F32 => 4,
68 PasturePrimitiveType::F64 => 8,
69 PasturePrimitiveType::Vec3u8 => 3,
70 PasturePrimitiveType::Vec3u16 => 6,
71 PasturePrimitiveType::Vec3f32 => 12,
72 PasturePrimitiveType::Vec3f64 => 24,
73 PasturePrimitiveType::Vec3i32 => 12,
74 &PasturePrimitiveType::Vec4u8 => 4,
75 }
76 }
77
78 fn as_token_stream(&self) -> quote::__private::TokenStream {
79 match self {
80 PasturePrimitiveType::U8 => quote! {pasture_core::layout::PointAttributeDataType::U8},
81 PasturePrimitiveType::I8 => quote! {pasture_core::layout::PointAttributeDataType::I8},
82 PasturePrimitiveType::U16 => quote! {pasture_core::layout::PointAttributeDataType::U16},
83 PasturePrimitiveType::I16 => quote! {pasture_core::layout::PointAttributeDataType::I16},
84 PasturePrimitiveType::U32 => quote! {pasture_core::layout::PointAttributeDataType::U32},
85 PasturePrimitiveType::I32 => quote! {pasture_core::layout::PointAttributeDataType::I32},
86 PasturePrimitiveType::U64 => quote! {pasture_core::layout::PointAttributeDataType::U64},
87 PasturePrimitiveType::I64 => quote! {pasture_core::layout::PointAttributeDataType::I64},
88 PasturePrimitiveType::F32 => quote! {pasture_core::layout::PointAttributeDataType::F32},
89 PasturePrimitiveType::F64 => quote! {pasture_core::layout::PointAttributeDataType::F64},
90 PasturePrimitiveType::Vec3u8 => {
91 quote! {pasture_core::layout::PointAttributeDataType::Vec3u8}
92 }
93 PasturePrimitiveType::Vec3u16 => {
94 quote! {pasture_core::layout::PointAttributeDataType::Vec3u16}
95 }
96 PasturePrimitiveType::Vec3f32 => {
97 quote! {pasture_core::layout::PointAttributeDataType::Vec3f32}
98 }
99 PasturePrimitiveType::Vec3f64 => {
100 quote! {pasture_core::layout::PointAttributeDataType::Vec3f64}
101 }
102 PasturePrimitiveType::Vec3i32 => {
103 quote! {pasture_core::layout::PointAttributeDataType::Vec3i32}
104 }
105 PasturePrimitiveType::Vec4u8 => {
106 quote! {pasture_core::layout::PointAttributeDataType::Vec4u8}
107 }
108 }
109 }
110}
111
112fn get_primitive_type_for_ident_type(ident: &Ident) -> Result<PasturePrimitiveType> {
113 let type_name = ident.to_string();
114 match type_name.as_str() {
115 "u8" => Ok(PasturePrimitiveType::U8),
116 "u16" => Ok(PasturePrimitiveType::U16),
117 "u32" => Ok(PasturePrimitiveType::U32),
118 "u64" => Ok(PasturePrimitiveType::U64),
119 "i8" => Ok(PasturePrimitiveType::I8),
120 "i16" => Ok(PasturePrimitiveType::I16),
121 "i32" => Ok(PasturePrimitiveType::I32),
122 "i64" => Ok(PasturePrimitiveType::I64),
123 "f32" => Ok(PasturePrimitiveType::F32),
124 "f64" => Ok(PasturePrimitiveType::F64),
125 _ => Err(Error::new_spanned(
126 ident,
127 format!("Type {} is no valid Pasture primitive type!", type_name),
128 )),
129 }
130}
131
132fn get_primitive_type_for_non_ident_type(type_path: &TypePath) -> Result<PasturePrimitiveType> {
133 let valid_idents: HashSet<_> = ["Vector3", "Vector4"].iter().collect();
135
136 let path_segment = type_path
137 .path
138 .segments
139 .first()
140 .ok_or_else(|| Error::new_spanned(&type_path.path, "Invalid type"))?;
141 if !valid_idents.contains(&path_segment.ident.to_string().as_str()) {
142 return Err(Error::new_spanned(&path_segment.ident, "Invalid type"));
143 }
144
145 let path_arg = match &path_segment.arguments {
146 PathArguments::AngleBracketed(arg) => arg,
147 _ => return Err(Error::new_spanned(&path_segment.arguments, "Invalid type")),
148 };
149
150 let first_generic_arg = path_arg
151 .args
152 .first()
153 .ok_or_else(|| Error::new_spanned(path_arg, "Invalid type arguments"))?;
154
155 let type_arg = match first_generic_arg {
156 GenericArgument::Type(t) => t,
157 _ => return Err(Error::new_spanned(first_generic_arg, "Invalid type")),
158 };
159
160 let type_path = match type_arg {
161 Type::Path(p) => p,
162 _ => return Err(Error::new_spanned(type_arg, "Invalid type")),
163 };
164
165 match type_path.path.get_ident() {
166 Some(ident) => {
167 let type_name = ident.to_string();
169 match path_segment.ident.to_string().as_str() {
170 "Vector3" => match type_name.as_str() {
171 "u8" => Ok(PasturePrimitiveType::Vec3u8),
172 "u16" => Ok(PasturePrimitiveType::Vec3u16),
173 "f32" => Ok(PasturePrimitiveType::Vec3f32),
174 "f64" => Ok(PasturePrimitiveType::Vec3f64),
175 "i32" => Ok(PasturePrimitiveType::Vec3i32),
176 _ => Err(Error::new_spanned(
177 ident,
178 format!("Vector3<{}> is no valid Pasture primitive type. Vector3 is supported, but only for generic argument(s) u8, u16, i32, f32 or f64", type_name),
179 ))
180 },
181 "Vector4" => match type_name.as_str() {
182 "u8" => Ok(PasturePrimitiveType::Vec4u8),
183 _ => Err(Error::new_spanned(
184 ident,
185 format!("Vector4<{}> is no valid Pasture primitive type. Vector4 is supported, but only for generic argument(s) u8", type_name),
186 ))
187 },
188 _ => Err(Error::new_spanned(ident, "Invalid type")),
189 }
190 }
191 None => Err(Error::new_spanned(&type_path.path, "Invalid type")),
192 }
193}
194
195fn type_path_to_primitive_type(type_path: &TypePath) -> Result<PasturePrimitiveType> {
196 if type_path.qself.is_some() {
197 return Err(Error::new_spanned(
198 type_path,
199 "Qualified types are illegal in a struct with #[derive(PointType)]",
200 ));
201 }
202
203 let datatype = match type_path.path.get_ident() {
204 Some(ident) => get_primitive_type_for_ident_type(ident),
205 None => get_primitive_type_for_non_ident_type(type_path),
206 }?;
207
208 Ok(datatype)
209 }
214
215fn get_attribute_name_from_field(field: &Field) -> Result<String> {
216 if field.attrs.len() != 1 {
217 return Err(Error::new_spanned(
218 field,
219 "derive(PointType) requires exactly one #[pasture] attribute per member!",
220 ));
221 }
222 let pasture_attribute = &field.attrs[0];
223 let meta = pasture_attribute.parse_meta()?;
224 let malformed_field_error_msg = "#[pasture] attribute is malformed. Correct syntax is #[pasture(attribute = \"NAME\")] or #[pasture(BUILTIN_XXX)], where XXX matches any of the builtin attributes in Pasture.";
226
227 match &meta {
231 syn::Meta::List(list) => {
232 let first_list_entry = list
233 .nested
234 .first()
235 .ok_or_else(|| Error::new_spanned(list, malformed_field_error_msg))?;
236 let nested_meta = match first_list_entry {
237 NestedMeta::Meta(nested_meta) => nested_meta,
238 _ => return Err(Error::new_spanned(list, malformed_field_error_msg)),
239 };
240
241 match nested_meta {
242 syn::Meta::Path(path) => {
243 let ident = path
244 .get_ident()
245 .ok_or_else(|| Error::new_spanned(path, malformed_field_error_msg))?;
246 let ident_as_str = ident.to_string();
247 match ident_as_str.as_str() {
248 "BUILTIN_POSITION_3D" => Ok("Position3D".into()),
249 "BUILTIN_INTENSITY" => Ok("Intensity".into()),
250 "BUILTIN_RETURN_NUMBER" => Ok("ReturnNumber".into()),
251 "BUILTIN_NUMBER_OF_RETURNS" => Ok("NumberOfReturns".into()),
252 "BUILTIN_CLASSIFICATION_FLAGS" => Ok("ClassificationFlags".into()),
253 "BUILTIN_SCANNER_CHANNEL" => Ok("ScannerChannel".into()),
254 "BUILTIN_SCAN_DIRECTION_FLAG" => Ok("ScanDirectionFlag".into()),
255 "BUILTIN_EDGE_OF_FLIGHT_LINE" => Ok("EdgeOfFlightLine".into()),
256 "BUILTIN_CLASSIFICATION" => Ok("Classification".into()),
257 "BUILTIN_SCAN_ANGLE_RANK" => Ok("ScanAngleRank".into()),
258 "BUILTIN_SCAN_ANGLE" => Ok("ScanAngle".into()),
259 "BUILTIN_USER_DATA" => Ok("UserData".into()),
260 "BUILTIN_POINT_SOURCE_ID" => Ok("PointSourceID".into()),
261 "BUILTIN_COLOR_RGB" => Ok("ColorRGB".into()),
262 "BUILTIN_GPS_TIME" => Ok("GpsTime".into()),
263 "BUILTIN_NIR" => Ok("NIR".into()),
264 "BUILTIN_WAVE_PACKET_DESCRIPTOR_INDEX" => {
265 Ok("WavePacketDescriptorIndex".into())
266 }
267 "BUILTIN_WAVEFORM_DATA_OFFSET" => Ok("WaveformDataOffset".into()),
268 "BUILTIN_WAVEFORM_PACKET_SIZE" => Ok("WaveformPacketSize".into()),
269 "BUILTIN_RETURN_POINT_WAVEFORM_LOCATION" => {
270 Ok("ReturnPointWaveformLocation".into())
271 }
272 "BUILTIN_WAVEFORM_PARAMETERS" => Ok("WaveformParameters".into()),
273 "BUILTIN_POINT_ID" => Ok("PointID".into()),
274 "BUILTIN_NORMAL" => Ok("Normal".into()),
275 _ => Err(Error::new_spanned(
277 ident,
278 format!("Unrecognized attribute name {}", ident_as_str),
279 )),
280 }
281 }
282 syn::Meta::NameValue(name_value) => name_value
283 .path
284 .get_ident()
285 .and_then(|path| {
286 if path != "attribute" {
287 return None;
288 }
289
290 if let Lit::Str(ref attribute_name) = name_value.lit {
291 Some(attribute_name.value())
292 } else {
293 None
294 }
295 })
296 .ok_or_else(|| Error::new_spanned(name_value, malformed_field_error_msg)),
297 bad => Err(Error::new_spanned(bad, malformed_field_error_msg)),
298 }
299 }
300 bad => Err(Error::new_spanned(bad, malformed_field_error_msg)),
301 }
302}
303
304struct FieldLayoutDescription {
307 pub attribute_name: String,
308 pub primitive_type: PasturePrimitiveType,
309}
310
311fn get_field_layout_descriptions(fields: &Fields) -> Result<Vec<FieldLayoutDescription>> {
312 fields
313 .iter()
314 .map(|field| match field.ty {
315 Type::Path(ref type_path) => {
316 let primitive_type = type_path_to_primitive_type(type_path)?;
317 let attribute_name = get_attribute_name_from_field(field)?;
318
319 Ok(FieldLayoutDescription {
320 attribute_name,
321 primitive_type,
322 })
323 }
324 ref bad => Err(Error::new_spanned(bad, "Invalid type in PointType struct")),
325 })
326 .collect::<Result<Vec<FieldLayoutDescription>>>()
327}
328
329fn field_parameters(data: &Data, ident: &Ident) -> Result<Vec<FieldLayoutDescription>> {
330 match data {
338 Data::Struct(struct_data) => get_field_layout_descriptions(&struct_data.fields),
339 _ => Err(Error::new_spanned(
340 ident,
341 "#[derive(PointType)] is only valid for structs",
342 )),
343 }
344}
345
346fn calculate_offsets_and_alignment(
347 fields: &[FieldLayoutDescription],
348 data: &Data,
349 ident: &Ident,
350 type_attributes: &[Attribute],
351) -> Result<(Vec<u64>, u64)> {
352 let struct_data = match data {
353 Data::Struct(struct_data) => struct_data,
354 _ => {
355 return Err(Error::new_spanned(
356 ident,
357 "#[derive(PointType)] is only valid for structs",
358 ))
359 }
360 };
361 let struct_layout = get_struct_member_layout(type_attributes, struct_data)?;
362
363 let mut current_offset = 0;
364 let mut max_alignment = 1;
365 let mut offsets = vec![];
366 for field in fields {
367 let min_alignment = match struct_layout {
368 StructMemberLayout::C => field.primitive_type.min_alignment(),
369 StructMemberLayout::Packed(max_alignment) => {
370 std::cmp::min(max_alignment, field.primitive_type.min_alignment())
371 }
372 };
373 max_alignment = std::cmp::max(min_alignment, max_alignment);
374
375 let aligned_offset = ((current_offset + min_alignment - 1) / min_alignment) * min_alignment;
376 offsets.push(aligned_offset);
377 current_offset = aligned_offset + field.primitive_type.size();
378 }
379
380 Ok((offsets, max_alignment))
381}
382
383#[proc_macro_derive(PointType, attributes(pasture))]
423pub fn derive_point_type(item: TokenStream) -> TokenStream {
424 let input = parse_macro_input!(item as DeriveInput);
425
426 if !input.generics.params.is_empty() {
435 return Error::new_spanned(input, "derive(PointType) is not valid for generic types")
436 .to_compile_error()
437 .into();
438 }
439
440 let name = &input.ident;
441
442 let fields = match field_parameters(&input.data, name) {
443 Ok(inner) => inner,
444 Err(why) => {
445 return why.to_compile_error().into();
446 }
447 };
448 let (offsets, type_alignment) =
449 match calculate_offsets_and_alignment(&fields, &input.data, name, input.attrs.as_slice()) {
450 Ok(inner) => inner,
451 Err(why) => {
452 return why.to_compile_error().into();
453 }
454 };
455
456 let attribute_descriptions = fields.iter().zip(offsets.iter()).map(|(field, offset)| {
457 let attribute_name = &field.attribute_name;
458 let primitive_type = &field.primitive_type.as_token_stream();
459 quote! {
460 pasture_core::layout::PointAttributeDefinition::custom(std::borrow::Cow::Borrowed(#attribute_name), #primitive_type).at_offset_in_type(#offset)
461 }
462 });
463
464 let gen = quote! {
465 impl pasture_core::layout::PointType for #name {
466 fn layout() -> pasture_core::layout::PointLayout {
467 pasture_core::layout::PointLayout::from_members_and_alignment(&[
468 #(#attribute_descriptions ,)*
469 ], #type_alignment)
470 }
471 }
472 };
473
474 gen.into()
475}