1use syn::ext::IdentExt;
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23enum PhysicalType {
24 Boolean,
25 Int32,
26 Int64,
27 Float,
28 Double,
29 ByteArray,
30 FixedLenByteArray,
31}
32
33#[derive(Debug, PartialEq)]
34pub struct Field {
35 ident: syn::Ident,
36 ty: Type,
37 is_a_byte_buf: bool,
38 third_party_type: Option<ThirdPartyType>,
39}
40
41#[derive(Debug, PartialEq)]
49enum ThirdPartyType {
50 ChronoNaiveDateTime,
51 ChronoNaiveDate,
52 Uuid,
53}
54
55impl Field {
56 pub fn from(f: &syn::Field) -> Self {
57 let ty = Type::from(f);
58 let is_a_byte_buf = ty.physical_type() == PhysicalType::ByteArray;
59
60 let third_party_type = match &ty.last_part()[..] {
61 "NaiveDateTime" => Some(ThirdPartyType::ChronoNaiveDateTime),
62 "NaiveDate" => Some(ThirdPartyType::ChronoNaiveDate),
63 "Uuid" => Some(ThirdPartyType::Uuid),
64 _ => None,
65 };
66
67 Field {
68 ident: f
69 .ident
70 .clone()
71 .expect("Only structs with named fields are currently supported"),
72 ty,
73 is_a_byte_buf,
74 third_party_type,
75 }
76 }
77
78 pub fn writer_snippet(&self) -> proc_macro2::TokenStream {
99 let ident = &self.ident;
100 let column_writer = self.ty.column_writer();
101
102 let vals_builder = match &self.ty {
103 Type::TypePath(_) => self.copied_direct_vals(),
104 Type::Option(first_type) => match **first_type {
105 Type::TypePath(_) => self.option_into_vals(),
106 Type::Reference(_, ref second_type) => match **second_type {
107 Type::TypePath(_) => self.option_into_vals(),
108 _ => unimplemented!("Unsupported type encountered"),
109 },
110 Type::Vec(ref first_type) => match **first_type {
111 Type::TypePath(_) => self.option_into_vals(),
112 _ => unimplemented!("Unsupported type encountered"),
113 },
114 ref f => unimplemented!("Unsupported: {:#?}", f),
115 },
116 Type::Reference(_, first_type) => match **first_type {
117 Type::TypePath(_) => self.copied_direct_vals(),
118 Type::Option(ref second_type) => match **second_type {
119 Type::TypePath(_) => self.option_into_vals(),
120 Type::Reference(_, ref second_type) => match **second_type {
121 Type::TypePath(_) => self.option_into_vals(),
122 Type::Slice(ref second_type) => match **second_type {
123 Type::TypePath(_) => self.option_into_vals(),
124 ref f => unimplemented!("Unsupported: {:#?}", f),
125 },
126 _ => unimplemented!("Unsupported type encountered"),
127 },
128 Type::Vec(ref first_type) => match **first_type {
129 Type::TypePath(_) => self.option_into_vals(),
130 _ => unimplemented!("Unsupported type encountered"),
131 },
132 ref f => unimplemented!("Unsupported: {:#?}", f),
133 },
134 Type::Slice(ref second_type) => match **second_type {
135 Type::TypePath(_) => self.copied_direct_vals(),
136 ref f => unimplemented!("Unsupported: {:#?}", f),
137 },
138 ref f => unimplemented!("Unsupported: {:#?}", f),
139 },
140 Type::Vec(first_type) => match **first_type {
141 Type::TypePath(_) => self.copied_direct_vals(),
142 ref f => unimplemented!("Unsupported: {:#?}", f),
143 },
144 f => unimplemented!("Unsupported: {:#?}", f),
145 };
146
147 let definition_levels = match &self.ty {
148 Type::TypePath(_) => None,
149 Type::Option(first_type) => match **first_type {
150 Type::TypePath(_) => Some(self.optional_definition_levels()),
151 Type::Option(_) => unimplemented!("Unsupported nesting encountered"),
152 Type::Reference(_, ref second_type)
153 | Type::Vec(ref second_type)
154 | Type::Array(ref second_type, _)
155 | Type::Slice(ref second_type) => match **second_type {
156 Type::TypePath(_) => Some(self.optional_definition_levels()),
157 _ => unimplemented!("Unsupported nesting encountered"),
158 },
159 },
160 Type::Reference(_, first_type)
161 | Type::Vec(first_type)
162 | Type::Array(first_type, _)
163 | Type::Slice(first_type) => match **first_type {
164 Type::TypePath(_) => None,
165 Type::Vec(ref second_type)
166 | Type::Array(ref second_type, _)
167 | Type::Slice(ref second_type) => match **second_type {
168 Type::TypePath(_) => None,
169 Type::Reference(_, ref third_type) => match **third_type {
170 Type::TypePath(_) => None,
171 _ => unimplemented!("Unsupported definition encountered"),
172 },
173 _ => unimplemented!("Unsupported definition encountered"),
174 },
175 Type::Reference(_, ref second_type) | Type::Option(ref second_type) => {
176 match **second_type {
177 Type::TypePath(_) => Some(self.optional_definition_levels()),
178 Type::Vec(ref third_type)
179 | Type::Array(ref third_type, _)
180 | Type::Slice(ref third_type) => match **third_type {
181 Type::TypePath(_) => Some(self.optional_definition_levels()),
182 Type::Reference(_, ref fourth_type) => match **fourth_type {
183 Type::TypePath(_) => Some(self.optional_definition_levels()),
184 _ => unimplemented!("Unsupported definition encountered"),
185 },
186 _ => unimplemented!("Unsupported definition encountered"),
187 },
188 Type::Reference(_, ref third_type) => match **third_type {
189 Type::TypePath(_) => Some(self.optional_definition_levels()),
190 Type::Slice(ref fourth_type) => match **fourth_type {
191 Type::TypePath(_) => Some(self.optional_definition_levels()),
192 _ => unimplemented!("Unsupported definition encountered"),
193 },
194 _ => unimplemented!("Unsupported definition encountered"),
195 },
196 _ => unimplemented!("Unsupported definition encountered"),
197 }
198 }
199 },
200 };
201
202 let write_batch_expr = if definition_levels.is_some() {
209 quote! {
210 if let #column_writer(typed) = column_writer.untyped() {
211 typed.write_batch(&vals[..], Some(&definition_levels[..]), None)?;
212 } else {
213 panic!("Schema and struct disagree on type for {}", stringify!{#ident})
214 }
215 }
216 } else {
217 quote! {
218 if let #column_writer(typed) = column_writer.untyped() {
219 typed.write_batch(&vals[..], None, None)?;
220 } else {
221 panic!("Schema and struct disagree on type for {}", stringify!{#ident})
222 }
223 }
224 };
225
226 quote! {
227 {
228 #definition_levels
229
230 #vals_builder
231
232 #write_batch_expr
233 }
234 }
235 }
236
237 pub fn reader_snippet(&self) -> proc_macro2::TokenStream {
260 let ident = &self.ident;
261 let column_reader = self.ty.column_reader();
262
263 let write_batch_expr = quote! {
265 let mut vals = Vec::new();
266 if let #column_reader(mut typed) = column_reader {
267 let mut definition_levels = Vec::new();
268 let (total_num, valid_num, decoded_num) = typed.read_records(
269 num_records, Some(&mut definition_levels), None, &mut vals)?;
270 if valid_num != decoded_num {
271 panic!("Support only valid records, found {} null records in column type {}",
272 decoded_num - valid_num, stringify!{#ident});
273 }
274 } else {
275 panic!("Schema and struct disagree on type for {}", stringify!{#ident});
276 }
277 };
278
279 let vals_writer = match &self.ty {
282 Type::TypePath(_) => self.copied_direct_fields(),
283 Type::Reference(_, first_type) => match **first_type {
284 Type::TypePath(_) => self.copied_direct_fields(),
285 Type::Slice(ref second_type) => match **second_type {
286 Type::TypePath(_) => self.copied_direct_fields(),
287 ref f => unimplemented!("Unsupported: {:#?}", f),
288 },
289 ref f => unimplemented!("Unsupported: {:#?}", f),
290 },
291 Type::Vec(first_type) => match **first_type {
292 Type::TypePath(_) => self.copied_direct_fields(),
293 ref f => unimplemented!("Unsupported: {:#?}", f),
294 },
295 f => unimplemented!("Unsupported: {:#?}", f),
296 };
297
298 quote! {
299 {
300 #write_batch_expr
301
302 #vals_writer
303 }
304 }
305 }
306
307 pub fn parquet_type(&self) -> proc_macro2::TokenStream {
308 let field_name = self.ident.unraw().to_string();
314 let physical_type = match self.ty.physical_type() {
315 PhysicalType::Boolean => quote! {
316 ::parquet::basic::Type::BOOLEAN
317 },
318 PhysicalType::Int32 => quote! {
319 ::parquet::basic::Type::INT32
320 },
321 PhysicalType::Int64 => quote! {
322 ::parquet::basic::Type::INT64
323 },
324 PhysicalType::Float => quote! {
325 ::parquet::basic::Type::FLOAT
326 },
327 PhysicalType::Double => quote! {
328 ::parquet::basic::Type::DOUBLE
329 },
330 PhysicalType::ByteArray => quote! {
331 ::parquet::basic::Type::BYTE_ARRAY
332 },
333 PhysicalType::FixedLenByteArray => quote! {
334 ::parquet::basic::Type::FIXED_LEN_BYTE_ARRAY
335 },
336 };
337 let logical_type = self.ty.logical_type();
338 let repetition = self.ty.repetition();
339 let converted_type = self.ty.converted_type();
340 let length = self.ty.length();
341
342 let mut builder = quote! {
343 ParquetType::primitive_type_builder(#field_name, #physical_type)
344 .with_logical_type(#logical_type)
345 .with_repetition(#repetition)
346 };
347
348 if let Some(converted_type) = converted_type {
349 builder = quote! { #builder.with_converted_type(#converted_type) };
350 }
351
352 if let Some(length) = length {
353 builder = quote! { #builder.with_length(#length) };
354 }
355
356 quote! { fields.push(#builder.build().unwrap().into()) }
357 }
358
359 fn option_into_vals(&self) -> proc_macro2::TokenStream {
360 let field_name = &self.ident;
361 let is_a_byte_buf = self.is_a_byte_buf;
362 let is_a_timestamp = self.third_party_type == Some(ThirdPartyType::ChronoNaiveDateTime);
363 let is_a_date = self.third_party_type == Some(ThirdPartyType::ChronoNaiveDate);
364 let is_a_uuid = self.third_party_type == Some(ThirdPartyType::Uuid);
365 let copy_to_vec = !matches!(
366 self.ty.physical_type(),
367 PhysicalType::ByteArray | PhysicalType::FixedLenByteArray
368 );
369
370 let binding = if copy_to_vec {
371 quote! { let Some(inner) = rec.#field_name }
372 } else {
373 quote! { let Some(inner) = &rec.#field_name }
374 };
375
376 let some = if is_a_timestamp {
377 quote! { Some(inner.timestamp_millis()) }
378 } else if is_a_date {
379 quote! { Some(inner.signed_duration_since(::chrono::NaiveDate::from_ymd(1970, 1, 1)).num_days() as i32) }
380 } else if is_a_uuid {
381 quote! { Some((&inner.to_string()[..]).into()) }
382 } else if is_a_byte_buf {
383 quote! { Some((&inner[..]).into())}
384 } else {
385 match self.ty.physical_type() {
387 PhysicalType::Int32 => quote! { Some(inner as i32) },
388 PhysicalType::Int64 => quote! { Some(inner as i64) },
389 _ => quote! { Some(inner) },
390 }
391 };
392
393 quote! {
394 let vals: Vec<_> = records.iter().filter_map(|rec| {
395 if #binding {
396 #some
397 } else {
398 None
399 }
400 }).collect();
401 }
402 }
403
404 fn copied_direct_vals(&self) -> proc_macro2::TokenStream {
406 let field_name = &self.ident;
407
408 let access = match self.third_party_type {
409 Some(ThirdPartyType::ChronoNaiveDateTime) => {
410 quote! { rec.#field_name.timestamp_millis() }
411 }
412 Some(ThirdPartyType::ChronoNaiveDate) => {
413 quote! { rec.#field_name.signed_duration_since(::chrono::NaiveDate::from_ymd(1970, 1, 1)).num_days() as i32 }
414 }
415 Some(ThirdPartyType::Uuid) => {
416 quote! { rec.#field_name.as_bytes().to_vec().into() }
417 }
418 _ => {
419 if self.is_a_byte_buf {
420 quote! { (&rec.#field_name[..]).into() }
421 } else {
422 match self.ty.physical_type() {
424 PhysicalType::Int32 => quote! { rec.#field_name as i32 },
425 PhysicalType::Int64 => quote! { rec.#field_name as i64 },
426 _ => quote! { rec.#field_name },
427 }
428 }
429 }
430 };
431
432 quote! {
433 let vals: Vec<_> = records.iter().map(|rec| #access).collect();
434 }
435 }
436
437 fn copied_direct_fields(&self) -> proc_macro2::TokenStream {
439 let field_name = &self.ident;
440
441 let value = match self.third_party_type {
442 Some(ThirdPartyType::ChronoNaiveDateTime) => {
443 quote! { ::chrono::naive::NaiveDateTime::from_timestamp_millis(vals[i]).unwrap() }
444 }
445 Some(ThirdPartyType::ChronoNaiveDate) => {
446 quote! {
448 ::chrono::naive::NaiveDate::from_num_days_from_ce_opt(vals[i].saturating_add(719163)).unwrap()
449 }
450 }
451 Some(ThirdPartyType::Uuid) => {
452 quote! { ::uuid::Uuid::from_bytes(vals[i].data().try_into().unwrap()) }
453 }
454 _ => match &self.ty {
455 Type::TypePath(_) => match self.ty.last_part().as_str() {
456 "String" => quote! { String::from(std::str::from_utf8(vals[i].data())
457 .expect("invalid UTF-8 sequence")) },
458 t => {
459 let s: proc_macro2::TokenStream = t.parse().unwrap();
460 quote! { vals[i] as #s }
461 }
462 },
463 Type::Vec(_) => quote! { vals[i].data().to_vec() },
464 f => unimplemented!("Unsupported: {:#?}", f),
465 },
466 };
467
468 quote! {
469 for (i, r) in &mut records[..num_records].iter_mut().enumerate() {
470 r.#field_name = #value;
471 }
472 }
473 }
474
475 fn optional_definition_levels(&self) -> proc_macro2::TokenStream {
476 let field_name = &self.ident;
477
478 quote! {
479 let definition_levels: Vec<i16> = self
480 .iter()
481 .map(|rec| if rec.#field_name.is_some() { 1 } else { 0 })
482 .collect();
483 }
484 }
485}
486
487#[allow(clippy::enum_variant_names)]
488#[allow(clippy::large_enum_variant)]
489#[derive(Debug, PartialEq)]
490enum Type {
491 Array(Box<Type>, syn::Expr),
492 Option(Box<Type>),
493 Slice(Box<Type>),
494 Vec(Box<Type>),
495 TypePath(syn::Type),
496 Reference(Option<syn::Lifetime>, Box<Type>),
497}
498
499impl Type {
500 fn column_writer(&self) -> syn::TypePath {
503 match self.physical_type() {
504 PhysicalType::Boolean => {
505 syn::parse_quote!(ColumnWriter::BoolColumnWriter)
506 }
507 PhysicalType::Int32 => syn::parse_quote!(ColumnWriter::Int32ColumnWriter),
508 PhysicalType::Int64 => syn::parse_quote!(ColumnWriter::Int64ColumnWriter),
509 PhysicalType::Float => syn::parse_quote!(ColumnWriter::FloatColumnWriter),
510 PhysicalType::Double => syn::parse_quote!(ColumnWriter::DoubleColumnWriter),
511 PhysicalType::ByteArray => {
512 syn::parse_quote!(ColumnWriter::ByteArrayColumnWriter)
513 }
514 PhysicalType::FixedLenByteArray => {
515 syn::parse_quote!(ColumnWriter::FixedLenByteArrayColumnWriter)
516 }
517 }
518 }
519
520 fn column_reader(&self) -> syn::TypePath {
523 match self.physical_type() {
524 PhysicalType::Boolean => {
525 syn::parse_quote!(ColumnReader::BoolColumnReader)
526 }
527 PhysicalType::Int32 => syn::parse_quote!(ColumnReader::Int32ColumnReader),
528 PhysicalType::Int64 => syn::parse_quote!(ColumnReader::Int64ColumnReader),
529 PhysicalType::Float => syn::parse_quote!(ColumnReader::FloatColumnReader),
530 PhysicalType::Double => syn::parse_quote!(ColumnReader::DoubleColumnReader),
531 PhysicalType::ByteArray => {
532 syn::parse_quote!(ColumnReader::ByteArrayColumnReader)
533 }
534 PhysicalType::FixedLenByteArray => {
535 syn::parse_quote!(ColumnReader::FixedLenByteArrayColumnReader)
536 }
537 }
538 }
539
540 fn leaf_type_recursive(&self) -> &Type {
550 Type::leaf_type_recursive_helper(self, None)
551 }
552
553 fn leaf_type_recursive_helper<'a>(ty: &'a Type, parent_ty: Option<&'a Type>) -> &'a Type {
554 match ty {
555 Type::TypePath(_) => parent_ty.unwrap_or(ty),
556 Type::Option(first_type)
557 | Type::Vec(first_type)
558 | Type::Array(first_type, _)
559 | Type::Slice(first_type)
560 | Type::Reference(_, first_type) => {
561 Type::leaf_type_recursive_helper(first_type, Some(ty))
562 }
563 }
564 }
565
566 fn inner_type(&self) -> &syn::Type {
570 let leaf_type = self.leaf_type_recursive();
571
572 match leaf_type {
573 Type::TypePath(type_) => type_,
574 Type::Option(first_type)
575 | Type::Vec(first_type)
576 | Type::Array(first_type, _)
577 | Type::Slice(first_type)
578 | Type::Reference(_, first_type) => match **first_type {
579 Type::TypePath(ref type_) => type_,
580 _ => unimplemented!("leaf_type() should only return shallow types"),
581 },
582 }
583 }
584
585 fn last_part(&self) -> String {
598 let inner_type = self.inner_type();
599 let inner_type_str = (quote! { #inner_type }).to_string();
600
601 inner_type_str
602 .split("::")
603 .last()
604 .unwrap()
605 .trim()
606 .to_string()
607 }
608
609 fn physical_type(&self) -> PhysicalType {
617 let last_part = self.last_part();
618 let leaf_type = self.leaf_type_recursive();
619
620 match leaf_type {
621 Type::Array(first_type, _length) => {
622 if let Type::TypePath(_) = **first_type {
623 if last_part == "u8" {
624 return PhysicalType::FixedLenByteArray;
625 }
626 }
627 }
628 Type::Vec(first_type) | Type::Slice(first_type) => {
629 if let Type::TypePath(_) = **first_type {
630 if last_part == "u8" {
631 return PhysicalType::ByteArray;
632 }
633 }
634 }
635 _ => (),
636 }
637
638 match last_part.trim() {
639 "bool" => PhysicalType::Boolean,
640 "u8" | "u16" | "u32" => PhysicalType::Int32,
641 "i8" | "i16" | "i32" | "NaiveDate" => PhysicalType::Int32,
642 "u64" | "i64" | "NaiveDateTime" => PhysicalType::Int64,
643 "usize" | "isize" => {
644 if usize::BITS == 64 {
645 PhysicalType::Int64
646 } else {
647 PhysicalType::Int32
648 }
649 }
650 "f32" => PhysicalType::Float,
651 "f64" => PhysicalType::Double,
652 "String" | "str" | "Arc < str >" => PhysicalType::ByteArray,
653 "Uuid" => PhysicalType::FixedLenByteArray,
654 f => unimplemented!("{} currently is not supported", f),
655 }
656 }
657
658 fn length(&self) -> Option<syn::Expr> {
659 let last_part = self.last_part();
660 let leaf_type = self.leaf_type_recursive();
661
662 if let Type::Array(first_type, length) = leaf_type {
664 if let Type::TypePath(_) = **first_type {
665 if last_part == "u8" {
666 return Some(length.clone());
667 }
668 }
669 }
670
671 match last_part.trim() {
672 "Uuid" => Some(syn::parse_quote!(16)),
674 _ => None,
675 }
676 }
677
678 fn logical_type(&self) -> proc_macro2::TokenStream {
679 let last_part = self.last_part();
680 let leaf_type = self.leaf_type_recursive();
681
682 match leaf_type {
683 Type::Array(first_type, _length) => {
684 if let Type::TypePath(_) = **first_type {
685 if last_part == "u8" {
686 return quote! { None };
687 }
688 }
689 }
690 Type::Vec(first_type) | Type::Slice(first_type) => {
691 if let Type::TypePath(_) = **first_type {
692 if last_part == "u8" {
693 return quote! { None };
694 }
695 }
696 }
697 _ => (),
698 }
699
700 match last_part.trim() {
701 "bool" => quote! { None },
702 "u8" => quote! { Some(LogicalType::integer(8, false)) },
703 "u16" => quote! { Some(LogicalType::integer(16, false)) },
704 "u32" => quote! { Some(LogicalType::integer(32, false)) },
705 "u64" => quote! { Some(LogicalType::integer(64, false)) },
706 "i8" => quote! { Some(LogicalType::integer(8, true)) },
707 "i16" => quote! { Some(LogicalType::integer(16, true)) },
708 "i32" | "i64" => quote! { None },
709 "usize" => {
710 quote! { Some(LogicalType::integer(usize::BITS as i8, false)) }
711 }
712 "isize" => {
713 quote! { Some(LogicalType::integer(usize::BITS as i8, true)) }
714 }
715 "NaiveDate" => quote! { Some(LogicalType::Date) },
716 "NaiveDateTime" => quote! { None },
717 "f32" | "f64" => quote! { None },
718 "String" | "str" | "Arc < str >" => quote! { Some(LogicalType::String) },
719 "Uuid" => quote! { Some(LogicalType::Uuid) },
720 f => unimplemented!("{} currently is not supported", f),
721 }
722 }
723
724 fn converted_type(&self) -> Option<proc_macro2::TokenStream> {
725 let last_part = self.last_part();
726
727 match last_part.trim() {
728 "NaiveDateTime" => Some(quote! { ::parquet::basic::ConvertedType::TIMESTAMP_MILLIS }),
729 _ => None,
730 }
731 }
732
733 fn repetition(&self) -> proc_macro2::TokenStream {
734 match self {
735 Type::Option(_) => quote! { ::parquet::basic::Repetition::OPTIONAL },
736 Type::Reference(_, ty) => ty.repetition(),
737 _ => quote! { ::parquet::basic::Repetition::REQUIRED },
738 }
739 }
740
741 fn from(f: &syn::Field) -> Self {
744 Type::from_type(f, &f.ty)
745 }
746
747 fn from_type(f: &syn::Field, ty: &syn::Type) -> Self {
748 match ty {
749 syn::Type::Path(p) => Type::from_type_path(f, p),
750 syn::Type::Reference(tr) => Type::from_type_reference(f, tr),
751 syn::Type::Array(ta) => Type::from_type_array(f, ta),
752 syn::Type::Slice(ts) => Type::from_type_slice(f, ts),
753 other => unimplemented!(
754 "Unable to derive {:?} - it is currently an unsupported type\n{:#?}",
755 f.ident.as_ref().unwrap(),
756 other
757 ),
758 }
759 }
760
761 fn from_type_path(f: &syn::Field, p: &syn::TypePath) -> Self {
762 let last_segment = p.path.segments.last().unwrap();
763
764 let is_vec = last_segment.ident == syn::Ident::new("Vec", proc_macro2::Span::call_site());
765 let is_option =
766 last_segment.ident == syn::Ident::new("Option", proc_macro2::Span::call_site());
767
768 if is_vec || is_option {
769 let generic_type = match &last_segment.arguments {
770 syn::PathArguments::AngleBracketed(angle_args) => {
771 assert_eq!(angle_args.args.len(), 1);
772 let first_arg = &angle_args.args[0];
773
774 match first_arg {
775 syn::GenericArgument::Type(typath) => typath.clone(),
776 other => unimplemented!("Unsupported: {:#?}", other),
777 }
778 }
779 other => unimplemented!("Unsupported: {:#?}", other),
780 };
781
782 if is_vec {
783 Type::Vec(Box::new(Type::from_type(f, &generic_type)))
784 } else {
785 Type::Option(Box::new(Type::from_type(f, &generic_type)))
786 }
787 } else {
788 Type::TypePath(syn::Type::Path(p.clone()))
789 }
790 }
791
792 fn from_type_reference(f: &syn::Field, tr: &syn::TypeReference) -> Self {
793 let lifetime = tr.lifetime.clone();
794 let inner_type = Type::from_type(f, tr.elem.as_ref());
795 Type::Reference(lifetime, Box::new(inner_type))
796 }
797
798 fn from_type_array(f: &syn::Field, ta: &syn::TypeArray) -> Self {
799 let inner_type = Type::from_type(f, ta.elem.as_ref());
800 Type::Array(Box::new(inner_type), ta.len.clone())
801 }
802
803 fn from_type_slice(f: &syn::Field, ts: &syn::TypeSlice) -> Self {
804 let inner_type = Type::from_type(f, ts.elem.as_ref());
805 Type::Slice(Box::new(inner_type))
806 }
807}
808
809#[cfg(test)]
810mod test {
811 use super::*;
812 use syn::{Data, DataStruct, DeriveInput};
813
814 fn extract_fields(input: proc_macro2::TokenStream) -> Vec<syn::Field> {
815 let input: DeriveInput = syn::parse2(input).unwrap();
816
817 let fields = match input.data {
818 Data::Struct(DataStruct { fields, .. }) => fields,
819 _ => panic!("Input must be a struct"),
820 };
821
822 fields.iter().map(|field| field.to_owned()).collect()
823 }
824
825 #[test]
826 fn test_generating_a_simple_writer_snippet() {
827 let snippet: proc_macro2::TokenStream = quote! {
828 struct ABoringStruct {
829 counter: usize,
830 }
831 };
832
833 let fields = extract_fields(snippet);
834 let counter = Field::from(&fields[0]);
835
836 let snippet = counter.writer_snippet().to_string();
837 assert_eq!(snippet,
838 (quote!{
839 {
840 let vals : Vec < _ > = records . iter ( ) . map ( | rec | rec . counter as i64 ) . collect ( );
841
842 if let ColumnWriter::Int64ColumnWriter ( typed ) = column_writer.untyped() {
843 typed . write_batch ( & vals [ .. ] , None , None ) ?;
844 } else {
845 panic!("Schema and struct disagree on type for {}" , stringify!{ counter } )
846 }
847 }
848 }).to_string()
849 )
850 }
851
852 #[test]
853 fn test_generating_a_simple_reader_snippet() {
854 let snippet: proc_macro2::TokenStream = quote! {
855 struct ABoringStruct {
856 counter: usize,
857 }
858 };
859
860 let fields = extract_fields(snippet);
861 let counter = Field::from(&fields[0]);
862
863 let snippet = counter.reader_snippet().to_string();
864 assert_eq!(
865 snippet,
866 (quote! {
867 {
868 let mut vals = Vec::new();
869 if let ColumnReader::Int64ColumnReader(mut typed) = column_reader {
870 let mut definition_levels = Vec::new();
871 let (total_num, valid_num, decoded_num) = typed.read_records(
872 num_records, Some(&mut definition_levels), None, &mut vals)?;
873 if valid_num != decoded_num {
874 panic!("Support only valid records, found {} null records in column type {}",
875 decoded_num - valid_num, stringify!{counter});
876 }
877 } else {
878 panic!("Schema and struct disagree on type for {}", stringify!{counter});
879 }
880 for (i, r) in &mut records[..num_records].iter_mut().enumerate() {
881 r.counter = vals[i] as usize;
882 }
883 }
884 })
885 .to_string()
886 )
887 }
888
889 #[test]
890 fn test_parquet_type_with_raw_identifier() {
891 let snippet: proc_macro2::TokenStream = quote! {
892 struct ABoringStruct {
893 r#type: i32,
894 }
895 };
896
897 let fields = extract_fields(snippet);
898 let r#type = Field::from(&fields[0]);
899
900 let snippet = r#type.parquet_type().to_string();
902 assert!(
903 snippet.contains("primitive_type_builder (\"type\""),
904 "{snippet}"
905 );
906 }
907
908 #[test]
909 fn test_optional_to_writer_snippet() {
910 let struct_def: proc_macro2::TokenStream = quote! {
911 struct StringBorrower<'a> {
912 optional_str: Option<&'a str>,
913 optional_string: Option<&String>,
914 optional_dumb_int: Option<&i32>,
915 }
916 };
917
918 let fields = extract_fields(struct_def);
919
920 let optional = Field::from(&fields[0]);
921 let snippet = optional.writer_snippet();
922 assert_eq!(snippet.to_string(),
923 (quote! {
924 {
925 let definition_levels : Vec < i16 > = self . iter ( ) . map ( | rec | if rec . optional_str . is_some ( ) { 1 } else { 0 } ) . collect ( ) ;
926
927 let vals: Vec <_> = records.iter().filter_map( |rec| {
928 if let Some ( inner ) = &rec . optional_str {
929 Some ( (&inner[..]).into() )
930 } else {
931 None
932 }
933 }).collect();
934
935 if let ColumnWriter::ByteArrayColumnWriter ( typed ) = column_writer.untyped() {
936 typed . write_batch ( & vals [ .. ] , Some(&definition_levels[..]) , None ) ? ;
937 } else {
938 panic!("Schema and struct disagree on type for {}" , stringify ! { optional_str } )
939 }
940 }
941 }
942 ).to_string());
943
944 let optional = Field::from(&fields[1]);
945 let snippet = optional.writer_snippet();
946 assert_eq!(snippet.to_string(),
947 (quote!{
948 {
949 let definition_levels : Vec < i16 > = self . iter ( ) . map ( | rec | if rec . optional_string . is_some ( ) { 1 } else { 0 } ) . collect ( ) ;
950
951 let vals: Vec <_> = records.iter().filter_map( |rec| {
952 if let Some ( inner ) = &rec . optional_string {
953 Some ( (&inner[..]).into() )
954 } else {
955 None
956 }
957 }).collect();
958
959 if let ColumnWriter::ByteArrayColumnWriter ( typed ) = column_writer.untyped() {
960 typed . write_batch ( & vals [ .. ] , Some(&definition_levels[..]) , None ) ? ;
961 } else {
962 panic!("Schema and struct disagree on type for {}" , stringify ! { optional_string } )
963 }
964 }
965 }).to_string());
966
967 let optional = Field::from(&fields[2]);
968 let snippet = optional.writer_snippet();
969 assert_eq!(snippet.to_string(),
970 (quote!{
971 {
972 let definition_levels : Vec < i16 > = self . iter ( ) . map ( | rec | if rec . optional_dumb_int . is_some ( ) { 1 } else { 0 } ) . collect ( ) ;
973
974 let vals: Vec <_> = records.iter().filter_map( |rec| {
975 if let Some ( inner ) = rec . optional_dumb_int {
976 Some ( inner as i32 )
977 } else {
978 None
979 }
980 }).collect();
981
982 if let ColumnWriter::Int32ColumnWriter ( typed ) = column_writer.untyped() {
983 typed . write_batch ( & vals [ .. ] , Some(&definition_levels[..]) , None ) ? ;
984 } else {
985 panic!("Schema and struct disagree on type for {}" , stringify ! { optional_dumb_int } )
986 }
987 }
988 }).to_string());
989 }
990
991 #[test]
992 fn test_converting_to_column_writer_type() {
993 let snippet: proc_macro2::TokenStream = quote! {
994 struct ABasicStruct {
995 yes_no: bool,
996 name: String,
997 }
998 };
999
1000 let fields = extract_fields(snippet);
1001 let processed: Vec<_> = fields.iter().map(Field::from).collect();
1002
1003 let column_writers: Vec<_> = processed
1004 .iter()
1005 .map(|field| field.ty.column_writer())
1006 .collect();
1007
1008 assert_eq!(
1009 column_writers,
1010 vec![
1011 syn::parse_quote!(ColumnWriter::BoolColumnWriter),
1012 syn::parse_quote!(ColumnWriter::ByteArrayColumnWriter)
1013 ]
1014 );
1015 }
1016
1017 #[test]
1018 fn test_converting_to_column_reader_type() {
1019 let snippet: proc_macro2::TokenStream = quote! {
1020 struct ABasicStruct {
1021 yes_no: bool,
1022 name: String,
1023 }
1024 };
1025
1026 let fields = extract_fields(snippet);
1027 let processed: Vec<_> = fields.iter().map(Field::from).collect();
1028
1029 let column_readers: Vec<_> = processed
1030 .iter()
1031 .map(|field| field.ty.column_reader())
1032 .collect();
1033
1034 assert_eq!(
1035 column_readers,
1036 vec![
1037 syn::parse_quote!(ColumnReader::BoolColumnReader),
1038 syn::parse_quote!(ColumnReader::ByteArrayColumnReader)
1039 ]
1040 );
1041 }
1042
1043 #[test]
1044 fn convert_basic_struct() {
1045 let snippet: proc_macro2::TokenStream = quote! {
1046 struct ABasicStruct {
1047 yes_no: bool,
1048 name: String,
1049 length: usize
1050 }
1051 };
1052
1053 let fields = extract_fields(snippet);
1054 let processed: Vec<_> = fields.iter().map(Field::from).collect();
1055 assert_eq!(processed.len(), 3);
1056
1057 assert_eq!(
1058 processed,
1059 vec![
1060 Field {
1061 ident: syn::Ident::new("yes_no", proc_macro2::Span::call_site()),
1062 ty: Type::TypePath(syn::parse_quote!(bool)),
1063 is_a_byte_buf: false,
1064 third_party_type: None,
1065 },
1066 Field {
1067 ident: syn::Ident::new("name", proc_macro2::Span::call_site()),
1068 ty: Type::TypePath(syn::parse_quote!(String)),
1069 is_a_byte_buf: true,
1070 third_party_type: None,
1071 },
1072 Field {
1073 ident: syn::Ident::new("length", proc_macro2::Span::call_site()),
1074 ty: Type::TypePath(syn::parse_quote!(usize)),
1075 is_a_byte_buf: false,
1076 third_party_type: None,
1077 }
1078 ]
1079 )
1080 }
1081
1082 #[test]
1083 fn test_get_inner_type() {
1084 let snippet: proc_macro2::TokenStream = quote! {
1085 struct LotsOfInnerTypes {
1086 a_vec: Vec<u8>,
1087 a_option: ::std::option::Option<bool>,
1088 a_silly_string: ::std::string::String,
1089 a_complicated_thing: ::std::option::Option<::std::result::Result<(),()>>,
1090 }
1091 };
1092
1093 let fields = extract_fields(snippet);
1094 let converted_fields: Vec<_> = fields.iter().map(Type::from).collect();
1095 let inner_types: Vec<_> = converted_fields
1096 .iter()
1097 .map(|field| field.inner_type())
1098 .collect();
1099 let inner_types_strs: Vec<_> = inner_types
1100 .iter()
1101 .map(|ty| (quote! { #ty }).to_string())
1102 .collect();
1103
1104 assert_eq!(
1105 inner_types_strs,
1106 vec![
1107 "u8",
1108 "bool",
1109 ":: std :: string :: String",
1110 ":: std :: result :: Result < () , () >"
1111 ]
1112 )
1113 }
1114
1115 #[test]
1116 fn test_physical_type() {
1117 let snippet: proc_macro2::TokenStream = quote! {
1118 struct LotsOfInnerTypes {
1119 a_buf: ::std::vec::Vec<u8>,
1120 a_number: i32,
1121 a_verbose_option: ::std::option::Option<bool>,
1122 a_silly_string: String,
1123 a_fix_byte_buf: [u8; 10],
1124 a_complex_option: ::std::option::Option<&Vec<u8>>,
1125 a_complex_vec: &::std::vec::Vec<&Option<u8>>,
1126 a_uuid: ::uuid::Uuid,
1127 }
1128 };
1129
1130 let fields = extract_fields(snippet);
1131 let converted_fields: Vec<_> = fields.iter().map(Type::from).collect();
1132 let physical_types: Vec<_> = converted_fields
1133 .iter()
1134 .map(|ty| ty.physical_type())
1135 .collect();
1136
1137 assert_eq!(
1138 physical_types,
1139 vec![
1140 PhysicalType::ByteArray,
1141 PhysicalType::Int32,
1142 PhysicalType::Boolean,
1143 PhysicalType::ByteArray,
1144 PhysicalType::FixedLenByteArray,
1145 PhysicalType::ByteArray,
1146 PhysicalType::Int32,
1147 PhysicalType::FixedLenByteArray,
1148 ]
1149 )
1150 }
1151
1152 #[test]
1153 fn test_type_length() {
1154 let snippet: proc_macro2::TokenStream = quote! {
1155 struct LotsOfInnerTypes {
1156 a_buf: ::std::vec::Vec<u8>,
1157 a_number: i32,
1158 a_verbose_option: ::std::option::Option<bool>,
1159 a_silly_string: String,
1160 a_fix_byte_buf: [u8; 10],
1161 a_complex_option: ::std::option::Option<&Vec<u8>>,
1162 a_complex_vec: &::std::vec::Vec<&Option<u8>>,
1163 a_uuid: ::uuid::Uuid,
1164 }
1165 };
1166
1167 let fields = extract_fields(snippet);
1168 let converted_fields: Vec<_> = fields.iter().map(Type::from).collect();
1169 let lengths: Vec<_> = converted_fields.iter().map(|ty| ty.length()).collect();
1170
1171 assert_eq!(
1172 lengths,
1173 vec![
1174 None,
1175 None,
1176 None,
1177 None,
1178 Some(syn::parse_quote!(10)),
1179 None,
1180 None,
1181 Some(syn::parse_quote!(16)),
1182 ]
1183 )
1184 }
1185
1186 #[test]
1187 fn test_convert_comprehensive_owned_struct() {
1188 let snippet: proc_macro2::TokenStream = quote! {
1189 struct VecHolder {
1190 a_vec: ::std::vec::Vec<u8>,
1191 a_option: ::std::option::Option<bool>,
1192 a_silly_string: ::std::string::String,
1193 a_complicated_thing: ::std::option::Option<::std::result::Result<(),()>>,
1194 }
1195 };
1196
1197 let fields = extract_fields(snippet);
1198 let converted_fields: Vec<_> = fields.iter().map(Type::from).collect();
1199
1200 assert_eq!(
1201 converted_fields,
1202 vec![
1203 Type::Vec(Box::new(Type::TypePath(syn::parse_quote!(u8)))),
1204 Type::Option(Box::new(Type::TypePath(syn::parse_quote!(bool)))),
1205 Type::TypePath(syn::parse_quote!(::std::string::String)),
1206 Type::Option(Box::new(Type::TypePath(
1207 syn::parse_quote!(::std::result::Result<(),()>)
1208 ))),
1209 ]
1210 );
1211 }
1212
1213 #[test]
1214 fn test_convert_borrowed_struct() {
1215 let snippet: proc_macro2::TokenStream = quote! {
1216 struct Borrower<'a> {
1217 a_str: &'a str,
1218 a_borrowed_option: &'a Option<bool>,
1219 so_many_borrows: &'a Option<&'a str>,
1220 }
1221 };
1222
1223 let fields = extract_fields(snippet);
1224 let types: Vec<_> = fields.iter().map(Type::from).collect();
1225
1226 assert_eq!(
1227 types,
1228 vec![
1229 Type::Reference(
1230 Some(syn::Lifetime::new("'a", proc_macro2::Span::call_site())),
1231 Box::new(Type::TypePath(syn::parse_quote!(str)))
1232 ),
1233 Type::Reference(
1234 Some(syn::Lifetime::new("'a", proc_macro2::Span::call_site())),
1235 Box::new(Type::Option(Box::new(Type::TypePath(syn::parse_quote!(
1236 bool
1237 )))))
1238 ),
1239 Type::Reference(
1240 Some(syn::Lifetime::new("'a", proc_macro2::Span::call_site())),
1241 Box::new(Type::Option(Box::new(Type::Reference(
1242 Some(syn::Lifetime::new("'a", proc_macro2::Span::call_site())),
1243 Box::new(Type::TypePath(syn::parse_quote!(str)))
1244 ))))
1245 ),
1246 ]
1247 );
1248 }
1249
1250 #[test]
1251 fn test_chrono_timestamp_millis_write() {
1252 let snippet: proc_macro2::TokenStream = quote! {
1253 struct ATimestampStruct {
1254 henceforth: chrono::NaiveDateTime,
1255 maybe_happened: Option<&chrono::NaiveDateTime>,
1256 }
1257 };
1258
1259 let fields = extract_fields(snippet);
1260 let when = Field::from(&fields[0]);
1261 assert_eq!(when.writer_snippet().to_string(),(quote!{
1262 {
1263 let vals : Vec<_> = records.iter().map(|rec| rec.henceforth.timestamp_millis() ).collect();
1264 if let ColumnWriter::Int64ColumnWriter(typed) = column_writer.untyped() {
1265 typed.write_batch(&vals[..], None, None) ?;
1266 } else {
1267 panic!("Schema and struct disagree on type for {}" , stringify!{ henceforth })
1268 }
1269 }
1270 }).to_string());
1271
1272 let maybe_happened = Field::from(&fields[1]);
1273 assert_eq!(maybe_happened.writer_snippet().to_string(),(quote!{
1274 {
1275 let definition_levels : Vec<i16> = self.iter().map(|rec| if rec.maybe_happened.is_some() { 1 } else { 0 }).collect();
1276 let vals : Vec<_> = records.iter().filter_map(|rec| {
1277 if let Some(inner) = rec.maybe_happened {
1278 Some(inner.timestamp_millis())
1279 } else {
1280 None
1281 }
1282 }).collect();
1283
1284 if let ColumnWriter::Int64ColumnWriter(typed) = column_writer.untyped() {
1285 typed.write_batch(&vals[..], Some(&definition_levels[..]), None) ?;
1286 } else {
1287 panic!("Schema and struct disagree on type for {}" , stringify!{ maybe_happened })
1288 }
1289 }
1290 }).to_string());
1291 }
1292
1293 #[test]
1294 fn test_chrono_timestamp_millis_read() {
1295 let snippet: proc_macro2::TokenStream = quote! {
1296 struct ATimestampStruct {
1297 henceforth: chrono::NaiveDateTime,
1298 }
1299 };
1300
1301 let fields = extract_fields(snippet);
1302 let when = Field::from(&fields[0]);
1303 assert_eq!(when.reader_snippet().to_string(),(quote!{
1304 {
1305 let mut vals = Vec::new();
1306 if let ColumnReader::Int64ColumnReader(mut typed) = column_reader {
1307 let mut definition_levels = Vec::new();
1308 let (total_num, valid_num, decoded_num) = typed.read_records(
1309 num_records, Some(&mut definition_levels), None, &mut vals)?;
1310 if valid_num != decoded_num {
1311 panic!("Support only valid records, found {} null records in column type {}",
1312 decoded_num - valid_num, stringify!{henceforth});
1313 }
1314 } else {
1315 panic!("Schema and struct disagree on type for {}", stringify!{ henceforth });
1316 }
1317 for (i, r) in &mut records[..num_records].iter_mut().enumerate() {
1318 r.henceforth = ::chrono::naive::NaiveDateTime::from_timestamp_millis(vals[i]).unwrap();
1319 }
1320 }
1321 }).to_string());
1322 }
1323
1324 #[test]
1325 fn test_chrono_date_write() {
1326 let snippet: proc_macro2::TokenStream = quote! {
1327 struct ATimestampStruct {
1328 henceforth: chrono::NaiveDate,
1329 maybe_happened: Option<&chrono::NaiveDate>,
1330 }
1331 };
1332
1333 let fields = extract_fields(snippet);
1334 let when = Field::from(&fields[0]);
1335 assert_eq!(when.writer_snippet().to_string(),(quote!{
1336 {
1337 let vals : Vec<_> = records.iter().map(|rec| rec.henceforth.signed_duration_since(::chrono::NaiveDate::from_ymd(1970, 1, 1)).num_days() as i32).collect();
1338 if let ColumnWriter::Int32ColumnWriter(typed) = column_writer.untyped() {
1339 typed.write_batch(&vals[..], None, None) ?;
1340 } else {
1341 panic!("Schema and struct disagree on type for {}" , stringify!{ henceforth })
1342 }
1343 }
1344 }).to_string());
1345
1346 let maybe_happened = Field::from(&fields[1]);
1347 assert_eq!(maybe_happened.writer_snippet().to_string(),(quote!{
1348 {
1349 let definition_levels : Vec<i16> = self.iter().map(|rec| if rec.maybe_happened.is_some() { 1 } else { 0 }).collect();
1350 let vals : Vec<_> = records.iter().filter_map(|rec| {
1351 if let Some(inner) = rec.maybe_happened {
1352 Some(inner.signed_duration_since(::chrono::NaiveDate::from_ymd(1970, 1, 1)).num_days() as i32)
1353 } else {
1354 None
1355 }
1356 }).collect();
1357
1358 if let ColumnWriter::Int32ColumnWriter(typed) = column_writer.untyped() {
1359 typed.write_batch(&vals[..], Some(&definition_levels[..]), None) ?;
1360 } else {
1361 panic!("Schema and struct disagree on type for {}" , stringify!{ maybe_happened })
1362 }
1363 }
1364 }).to_string());
1365 }
1366
1367 #[test]
1368 fn test_chrono_date_read() {
1369 let snippet: proc_macro2::TokenStream = quote! {
1370 struct ATimestampStruct {
1371 henceforth: chrono::NaiveDate,
1372 }
1373 };
1374
1375 let fields = extract_fields(snippet);
1376 let when = Field::from(&fields[0]);
1377 assert_eq!(when.reader_snippet().to_string(),(quote!{
1378 {
1379 let mut vals = Vec::new();
1380 if let ColumnReader::Int32ColumnReader(mut typed) = column_reader {
1381 let mut definition_levels = Vec::new();
1382 let (total_num, valid_num, decoded_num) = typed.read_records(
1383 num_records, Some(&mut definition_levels), None, &mut vals)?;
1384 if valid_num != decoded_num {
1385 panic!("Support only valid records, found {} null records in column type {}",
1386 decoded_num - valid_num, stringify!{henceforth});
1387 }
1388 } else {
1389 panic!("Schema and struct disagree on type for {}", stringify!{ henceforth });
1390 }
1391 for (i, r) in &mut records[..num_records].iter_mut().enumerate() {
1392 r.henceforth = ::chrono::naive::NaiveDate::from_num_days_from_ce_opt(vals[i].saturating_add(719163)).unwrap();
1393 }
1394 }
1395 }).to_string());
1396 }
1397
1398 #[test]
1399 fn test_uuid_write() {
1400 let snippet: proc_macro2::TokenStream = quote! {
1401 struct AUuidStruct {
1402 unique_id: uuid::Uuid,
1403 maybe_unique_id: Option<&uuid::Uuid>,
1404 }
1405 };
1406
1407 let fields = extract_fields(snippet);
1408 let when = Field::from(&fields[0]);
1409 assert_eq!(when.writer_snippet().to_string(),(quote!{
1410 {
1411 let vals : Vec<_> = records.iter().map(|rec| rec.unique_id.as_bytes().to_vec().into() ).collect();
1412 if let ColumnWriter::FixedLenByteArrayColumnWriter(typed) = column_writer.untyped() {
1413 typed.write_batch(&vals[..], None, None) ?;
1414 } else {
1415 panic!("Schema and struct disagree on type for {}" , stringify!{ unique_id })
1416 }
1417 }
1418 }).to_string());
1419
1420 let maybe_happened = Field::from(&fields[1]);
1421 assert_eq!(maybe_happened.writer_snippet().to_string(),(quote!{
1422 {
1423 let definition_levels : Vec<i16> = self.iter().map(|rec| if rec.maybe_unique_id.is_some() { 1 } else { 0 }).collect();
1424 let vals : Vec<_> = records.iter().filter_map(|rec| {
1425 if let Some(inner) = &rec.maybe_unique_id {
1426 Some((&inner.to_string()[..]).into())
1427 } else {
1428 None
1429 }
1430 }).collect();
1431
1432 if let ColumnWriter::FixedLenByteArrayColumnWriter(typed) = column_writer.untyped() {
1433 typed.write_batch(&vals[..], Some(&definition_levels[..]), None) ?;
1434 } else {
1435 panic!("Schema and struct disagree on type for {}" , stringify!{ maybe_unique_id })
1436 }
1437 }
1438 }).to_string());
1439 }
1440
1441 #[test]
1442 fn test_uuid_read() {
1443 let snippet: proc_macro2::TokenStream = quote! {
1444 struct AUuidStruct {
1445 unique_id: uuid::Uuid,
1446 }
1447 };
1448
1449 let fields = extract_fields(snippet);
1450 let when = Field::from(&fields[0]);
1451 assert_eq!(when.reader_snippet().to_string(),(quote!{
1452 {
1453 let mut vals = Vec::new();
1454 if let ColumnReader::FixedLenByteArrayColumnReader(mut typed) = column_reader {
1455 let mut definition_levels = Vec::new();
1456 let (total_num, valid_num, decoded_num) = typed.read_records(
1457 num_records, Some(&mut definition_levels), None, &mut vals)?;
1458 if valid_num != decoded_num {
1459 panic!("Support only valid records, found {} null records in column type {}",
1460 decoded_num - valid_num, stringify!{unique_id});
1461 }
1462 } else {
1463 panic!("Schema and struct disagree on type for {}", stringify!{ unique_id });
1464 }
1465 for (i, r) in &mut records[..num_records].iter_mut().enumerate() {
1466 r.unique_id = ::uuid::Uuid::from_bytes(vals[i].data().try_into().unwrap());
1467 }
1468 }
1469 }).to_string());
1470 }
1471
1472 #[test]
1473 fn test_converted_type() {
1474 let snippet: proc_macro2::TokenStream = quote! {
1475 struct ATimeStruct {
1476 time: chrono::NaiveDateTime,
1477 }
1478 };
1479
1480 let fields = extract_fields(snippet);
1481
1482 let time = Field::from(&fields[0]);
1483
1484 let converted_type = time.ty.converted_type();
1485 assert_eq!(
1486 converted_type.unwrap().to_string(),
1487 quote! { ::parquet::basic::ConvertedType::TIMESTAMP_MILLIS }.to_string()
1488 );
1489 }
1490}