1use arrow::array::ArrowNativeTypeOp;
21use arrow::compute::{
22 CastOptions, DecimalCast, cast_num_to_bool, cast_single_string_to_boolean_default, num_cast,
23 parse_string_to_decimal_native, rescale_decimal, single_bool_to_numeric,
24 single_decimal_to_float_lossy, single_float_to_decimal,
25};
26use arrow::datatypes::{
27 self, ArrowPrimitiveType, ArrowTimestampType, Decimal32Type, Decimal64Type, Decimal128Type,
28 Decimal256Type, DecimalType,
29};
30use arrow::error::{ArrowError, Result};
31use chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime, Timelike, Utc};
32use half::f16;
33use num_traits::NumCast;
34use parquet_variant::{Variant, VariantDecimal4, VariantDecimal8, VariantDecimal16};
35
36pub(crate) trait PrimitiveFromVariant: ArrowPrimitiveType {
38 fn from_variant(variant: &Variant<'_, '_>, shred: bool) -> Option<Self::Native>;
39}
40
41pub(crate) trait TimestampFromVariant<const NTZ: bool>: ArrowTimestampType {
45 fn from_variant(variant: &Variant<'_, '_>, shred: bool) -> Option<Self::Native>;
46}
47
48pub(crate) fn variant_cast_with_options<'a, 'm, 'v, T>(
54 variant: &'a Variant<'m, 'v>,
55 cast_options: &CastOptions<'_>,
56 cast: impl FnOnce(&'a Variant<'m, 'v>) -> Option<T>,
57) -> Result<Option<T>> {
58 if let Some(value) = cast(variant) {
59 Ok(Some(value))
60 } else if matches!(variant, Variant::Null) || cast_options.safe {
61 Ok(None)
62 } else {
63 Err(ArrowError::CastError(format!(
64 "Failed to cast variant value {variant:?}"
65 )))
66 }
67}
68
69macro_rules! impl_primitive_from_variant {
71 ($arrow_type:ty, $shred_fun:expr, $get_method:ident $(, $cast_fn:expr)?) => {
72 impl PrimitiveFromVariant for $arrow_type {
73 fn from_variant(variant: &Variant<'_, '_>, shred: bool) -> Option<Self::Native> {
74 let value = match shred {
75 true => $shred_fun(variant),
76 false => $get_method(variant),
77 };
78 $( let value = value.and_then($cast_fn); )?
79 value
80 }
81 }
82 };
83}
84
85macro_rules! impl_timestamp_from_variant {
86 ($timestamp_type:ty, $shred_fun:expr, $variant_method:expr, ntz=$ntz:ident, $cast_fn:expr $(,)?) => {
87 impl TimestampFromVariant<{ $ntz }> for $timestamp_type {
88 fn from_variant(variant: &Variant<'_, '_>, shred: bool) -> Option<Self::Native> {
89 let value = match shred {
90 true => ($shred_fun)(variant),
91 false => $variant_method(variant),
92 };
93
94 value.and_then($cast_fn)
95 }
96 }
97 };
98}
99
100fn convert_to_timestamp_nano(value: &Variant) -> Option<DateTime<Utc>> {
101 match *value {
102 Variant::TimestampNanos(d) | Variant::TimestampMicros(d) => Some(d),
103 _ => None,
104 }
105}
106
107fn convert_to_timestamp_ntz_nano(value: &Variant) -> Option<NaiveDateTime> {
108 match *value {
109 Variant::TimestampNtzNanos(d) | Variant::TimestampNtzMicros(d) => Some(d),
110 _ => None,
111 }
112}
113
114enum NumericKind {
115 Integer,
116 Float,
117}
118
119trait DecimalCastTarget: NumCast + Default {
120 const KIND: NumericKind;
121}
122
123macro_rules! impl_decimal_cast_target {
124 ($raw_type: ident, $target_kind:expr) => {
125 impl DecimalCastTarget for $raw_type {
126 const KIND: NumericKind = $target_kind;
127 }
128 };
129}
130
131impl_decimal_cast_target!(i8, NumericKind::Integer);
132impl_decimal_cast_target!(i16, NumericKind::Integer);
133impl_decimal_cast_target!(i32, NumericKind::Integer);
134impl_decimal_cast_target!(i64, NumericKind::Integer);
135impl_decimal_cast_target!(u8, NumericKind::Integer);
136impl_decimal_cast_target!(u16, NumericKind::Integer);
137impl_decimal_cast_target!(u32, NumericKind::Integer);
138impl_decimal_cast_target!(u64, NumericKind::Integer);
139impl_decimal_cast_target!(f16, NumericKind::Float);
140impl_decimal_cast_target!(f32, NumericKind::Float);
141impl_decimal_cast_target!(f64, NumericKind::Float);
142
143fn as_num<T>(variant: &Variant) -> Option<T>
149where
150 T: DecimalCastTarget,
151{
152 match *variant {
153 Variant::BooleanFalse => single_bool_to_numeric(false),
154 Variant::BooleanTrue => single_bool_to_numeric(true),
155 Variant::Int8(i) => num_cast(i),
156 Variant::Int16(i) => num_cast(i),
157 Variant::Int32(i) => num_cast(i),
158 Variant::Int64(i) => num_cast(i),
159 Variant::Float(f) => num_cast(f),
160 Variant::Double(d) => num_cast(d),
161 Variant::Decimal4(d) => {
162 cast_decimal_to_num::<Decimal32Type, T, _>(d.integer(), d.scale(), |x| x as f64)
163 }
164 Variant::Decimal8(d) => {
165 cast_decimal_to_num::<Decimal64Type, T, _>(d.integer(), d.scale(), |x| x as f64)
166 }
167 Variant::Decimal16(d) => {
168 cast_decimal_to_num::<Decimal128Type, T, _>(d.integer(), d.scale(), |x| x as f64)
169 }
170 _ => None,
171 }
172}
173
174fn cast_decimal_to_num<D, T, F>(raw: D::Native, scale: u8, as_float: F) -> Option<T>
175where
176 D: DecimalType,
177 D::Native: NumCast + ArrowNativeTypeOp,
178 T: DecimalCastTarget,
179 F: Fn(D::Native) -> f64,
180{
181 let base: D::Native = NumCast::from(10)?;
182
183 let div = base.pow_checked(<u32 as From<u8>>::from(scale)).ok()?;
184 match T::KIND {
185 NumericKind::Integer => raw
186 .div_checked(div)
187 .ok()
188 .and_then(<T as NumCast>::from::<D::Native>),
189 NumericKind::Float => T::from(single_decimal_to_float_lossy::<D, _>(
190 &as_float,
191 raw,
192 <i32 as From<u8>>::from(scale),
193 )),
194 }
195}
196
197fn cast_naive_date(value: &Variant<'_, '_>) -> Option<NaiveDate> {
198 value.as_naive_date()
199}
200
201fn cast_time_utc(value: &Variant<'_, '_>) -> Option<NaiveTime> {
202 value.as_time_utc()
203}
204
205fn always_none<T>(_input: &Variant) -> Option<T> {
207 None
208}
209
210impl_primitive_from_variant!(datatypes::Int32Type, Variant::as_int32, as_num);
211impl_primitive_from_variant!(datatypes::Int16Type, Variant::as_int16, as_num);
212impl_primitive_from_variant!(datatypes::Int8Type, Variant::as_int8, as_num);
213impl_primitive_from_variant!(datatypes::Int64Type, Variant::as_int64, as_num);
214impl_primitive_from_variant!(datatypes::UInt8Type, always_none, as_num);
215impl_primitive_from_variant!(datatypes::UInt16Type, always_none, as_num);
216impl_primitive_from_variant!(datatypes::UInt32Type, always_none, as_num);
217impl_primitive_from_variant!(datatypes::UInt64Type, always_none, as_num);
218impl_primitive_from_variant!(datatypes::Float16Type, always_none, as_num);
219impl_primitive_from_variant!(datatypes::Float32Type, Variant::as_f32, as_num);
220impl_primitive_from_variant!(datatypes::Float64Type, Variant::as_f64, as_num);
221impl_primitive_from_variant!(
222 datatypes::Date32Type,
223 Variant::as_naive_date,
224 cast_naive_date,
225 |v| { Some(datatypes::Date32Type::from_naive_date(v)) }
226);
227impl_primitive_from_variant!(
228 datatypes::Date64Type,
229 Variant::as_naive_date,
230 cast_naive_date,
231 |v| { Some(datatypes::Date64Type::from_naive_date(v)) }
232);
233impl_primitive_from_variant!(
234 datatypes::Time32SecondType,
235 always_none, cast_time_utc,
237 |v| {
238 if v.nanosecond() != 0 {
240 None
241 } else {
242 Some(v.num_seconds_from_midnight() as i32)
243 }
244 }
245);
246impl_primitive_from_variant!(
247 datatypes::Time32MillisecondType,
248 always_none, cast_time_utc,
250 |v| {
251 if v.nanosecond() % 1_000_000 != 0 {
253 None
254 } else {
255 Some(
256 (v.num_seconds_from_midnight() * 1_000) as i32
257 + (v.nanosecond() / 1_000_000) as i32,
258 )
259 }
260 }
261);
262impl_primitive_from_variant!(
263 datatypes::Time64MicrosecondType,
264 Variant::as_time_utc,
265 cast_time_utc,
266 |v| { Some(v.num_seconds_from_midnight() as i64 * 1_000_000 + v.nanosecond() as i64 / 1_000) }
267);
268impl_primitive_from_variant!(
269 datatypes::Time64NanosecondType,
270 always_none, cast_time_utc,
272 |v| {
273 Some(v.num_seconds_from_midnight() as i64 * 1_000_000_000 + v.nanosecond() as i64)
275 }
276);
277impl_timestamp_from_variant!(
278 datatypes::TimestampSecondType,
279 always_none, convert_to_timestamp_ntz_nano,
281 ntz = true,
282 |timestamp| {
283 if timestamp.nanosecond() != 0 {
285 None
286 } else {
287 Self::from_naive_datetime(timestamp, None)
288 }
289 }
290);
291impl_timestamp_from_variant!(
292 datatypes::TimestampSecondType,
293 always_none, convert_to_timestamp_nano,
295 ntz = false,
296 |timestamp| {
297 if timestamp.nanosecond() != 0 {
299 None
300 } else {
301 Self::from_naive_datetime(timestamp.naive_utc(), None)
302 }
303 }
304);
305impl_timestamp_from_variant!(
306 datatypes::TimestampMillisecondType,
307 always_none, convert_to_timestamp_ntz_nano,
309 ntz = true,
310 |timestamp| {
311 if timestamp.nanosecond() % 1_000_000 != 0 {
313 None
314 } else {
315 Self::from_naive_datetime(timestamp, None)
316 }
317 }
318);
319impl_timestamp_from_variant!(
320 datatypes::TimestampMillisecondType,
321 always_none, convert_to_timestamp_nano,
323 ntz = false,
324 |timestamp| {
325 if timestamp.nanosecond() % 1_000_000 != 0 {
327 None
328 } else {
329 Self::from_naive_datetime(timestamp.naive_utc(), None)
330 }
331 }
332);
333impl_timestamp_from_variant!(
334 datatypes::TimestampMicrosecondType,
335 Variant::as_timestamp_ntz_micros,
336 Variant::as_timestamp_ntz_micros,
337 ntz = true,
338 |timestamp| Self::from_naive_datetime(timestamp, None),
339);
340impl_timestamp_from_variant!(
341 datatypes::TimestampMicrosecondType,
342 Variant::as_timestamp_micros,
343 Variant::as_timestamp_micros,
344 ntz = false,
345 |timestamp| Self::from_naive_datetime(timestamp.naive_utc(), None)
346);
347impl_timestamp_from_variant!(
348 datatypes::TimestampNanosecondType,
349 Variant::as_timestamp_ntz_nanos,
350 convert_to_timestamp_ntz_nano,
351 ntz = true,
352 |timestamp| Self::from_naive_datetime(timestamp, None)
353);
354impl_timestamp_from_variant!(
355 datatypes::TimestampNanosecondType,
356 Variant::as_timestamp_nanos,
357 convert_to_timestamp_nano,
358 ntz = false,
359 |timestamp| Self::from_naive_datetime(timestamp.naive_utc(), None)
360);
361
362pub(crate) fn variant_to_unscaled_decimal<O>(
375 variant: &Variant<'_, '_>,
376 precision: u8,
377 scale: i8,
378) -> Option<O::Native>
379where
380 O: DecimalType,
381 O::Native: DecimalCast,
382{
383 let mul = 10_f64.powi(scale as i32);
384
385 match variant {
386 Variant::Int8(i) => rescale_decimal::<Decimal32Type, O>(
387 *i as i32,
388 VariantDecimal4::MAX_PRECISION,
389 0,
390 precision,
391 scale,
392 ),
393 Variant::Int16(i) => rescale_decimal::<Decimal32Type, O>(
394 *i as i32,
395 VariantDecimal4::MAX_PRECISION,
396 0,
397 precision,
398 scale,
399 ),
400 Variant::Int32(i) => rescale_decimal::<Decimal32Type, O>(
401 *i,
402 VariantDecimal4::MAX_PRECISION,
403 0,
404 precision,
405 scale,
406 ),
407 Variant::Int64(i) => rescale_decimal::<Decimal64Type, O>(
408 *i,
409 VariantDecimal8::MAX_PRECISION,
410 0,
411 precision,
412 scale,
413 ),
414 Variant::Float(f) => single_float_to_decimal::<O>(<f64 as From<f32>>::from(*f), mul),
415 Variant::Double(f) => single_float_to_decimal::<O>(*f, mul),
416 Variant::String(v) if scale >= 0 => parse_string_to_decimal_native::<O>(v, scale as _).ok(),
419 Variant::ShortString(v) if scale >= 0 => {
420 parse_string_to_decimal_native::<O>(v, scale as _).ok()
421 }
422 Variant::Decimal4(d) => rescale_decimal::<Decimal32Type, O>(
423 d.integer(),
424 VariantDecimal4::MAX_PRECISION,
425 d.scale() as i8,
426 precision,
427 scale,
428 ),
429 Variant::Decimal8(d) => rescale_decimal::<Decimal64Type, O>(
430 d.integer(),
431 VariantDecimal8::MAX_PRECISION,
432 d.scale() as i8,
433 precision,
434 scale,
435 ),
436 Variant::Decimal16(d) => rescale_decimal::<Decimal128Type, O>(
437 d.integer(),
438 VariantDecimal16::MAX_PRECISION,
439 d.scale() as i8,
440 precision,
441 scale,
442 ),
443 _ => None,
444 }
445}
446
447pub(crate) fn shred_variant_to_unscaled_decimal<O>(
453 variant: &Variant<'_, '_>,
454 precision: u8,
455 scale: i8,
456) -> Option<O::Native>
457where
458 O: ShredDecimalVariant,
459 O::Native: DecimalCast,
460{
461 match variant {
462 Variant::Int8(_)
463 | Variant::Int16(_)
464 | Variant::Int32(_)
465 | Variant::Int64(_)
466 | Variant::Decimal4(_)
467 | Variant::Decimal8(_)
468 | Variant::Decimal16(_) => O::shred_variant(variant, precision, scale),
469 _ => None,
470 }
471}
472pub(crate) trait ShredDecimalVariant: DecimalType {
473 fn shred_variant(value: &Variant<'_, '_>, precision: u8, scale: i8) -> Option<Self::Native>;
474}
475
476fn convert_to_unscaled_decimal<I, O>(
477 input: I::Native,
478 input_precision: u8,
479 input_scale: i8,
480 target_precision: u8,
481 target_scale: i8,
482) -> Option<O::Native>
483where
484 I: DecimalType,
485 O: DecimalType,
486 I::Native: DecimalCast,
487 O::Native: DecimalCast,
488{
489 let converted = rescale_decimal::<I, O>(
490 input,
491 input_precision,
492 input_scale,
493 target_precision,
494 target_scale,
495 )?;
496
497 let converted_back = rescale_decimal::<O, I>(
498 converted,
499 target_precision,
500 target_scale,
501 input_precision,
502 input_scale,
503 )?;
504 if converted_back == input {
505 return Some(converted);
506 }
507
508 None
509}
510
511impl ShredDecimalVariant for Decimal32Type {
512 fn shred_variant(value: &Variant<'_, '_>, precision: u8, scale: i8) -> Option<Self::Native> {
513 match *value {
514 Variant::Int8(i) => convert_to_unscaled_decimal::<Decimal32Type, Decimal32Type>(
515 i as i32,
516 VariantDecimal4::MAX_PRECISION,
517 0,
518 precision,
519 scale,
520 ),
521 Variant::Int16(i) => convert_to_unscaled_decimal::<Decimal32Type, Decimal32Type>(
522 i as i32,
523 VariantDecimal4::MAX_PRECISION,
524 0,
525 precision,
526 scale,
527 ),
528 Variant::Int32(i) => convert_to_unscaled_decimal::<Decimal32Type, Decimal32Type>(
529 i,
530 VariantDecimal4::MAX_PRECISION,
531 0,
532 precision,
533 scale,
534 ),
535 Variant::Int64(i) => {
536 let i32_value = <i64 as TryInto<i32>>::try_into(i).ok()?;
537 convert_to_unscaled_decimal::<Decimal32Type, Decimal32Type>(
538 i32_value,
539 VariantDecimal4::MAX_PRECISION,
540 0,
541 precision,
542 scale,
543 )
544 }
545 Variant::Decimal4(d) => convert_to_unscaled_decimal::<Decimal32Type, Decimal32Type>(
546 d.integer(),
547 VariantDecimal4::MAX_PRECISION,
548 d.scale() as i8,
549 precision,
550 scale,
551 ),
552 Variant::Decimal8(d) => convert_to_unscaled_decimal::<Decimal64Type, Decimal32Type>(
553 d.integer(),
554 VariantDecimal8::MAX_PRECISION,
555 d.scale() as i8,
556 precision,
557 scale,
558 ),
559 Variant::Decimal16(d) => convert_to_unscaled_decimal::<Decimal128Type, Decimal32Type>(
560 d.integer(),
561 VariantDecimal16::MAX_PRECISION,
562 d.scale() as i8,
563 precision,
564 scale,
565 ),
566 _ => None,
567 }
568 }
569}
570
571impl ShredDecimalVariant for Decimal64Type {
572 fn shred_variant(value: &Variant<'_, '_>, precision: u8, scale: i8) -> Option<Self::Native> {
573 match *value {
574 Variant::Int8(i) => convert_to_unscaled_decimal::<Decimal64Type, Decimal64Type>(
575 i as i64,
576 VariantDecimal8::MAX_PRECISION,
577 0,
578 precision,
579 scale,
580 ),
581 Variant::Int16(i) => convert_to_unscaled_decimal::<Decimal64Type, Decimal64Type>(
582 i as i64,
583 VariantDecimal8::MAX_PRECISION,
584 0,
585 precision,
586 scale,
587 ),
588 Variant::Int32(i) => convert_to_unscaled_decimal::<Decimal64Type, Decimal64Type>(
589 i as i64,
590 VariantDecimal8::MAX_PRECISION,
591 0,
592 precision,
593 scale,
594 ),
595 Variant::Int64(i) => convert_to_unscaled_decimal::<Decimal64Type, Decimal64Type>(
596 i,
597 VariantDecimal8::MAX_PRECISION,
598 0,
599 precision,
600 scale,
601 ),
602 Variant::Decimal4(d) => convert_to_unscaled_decimal::<Decimal32Type, Decimal64Type>(
603 d.integer(),
604 VariantDecimal4::MAX_PRECISION,
605 d.scale() as i8,
606 precision,
607 scale,
608 ),
609 Variant::Decimal8(d) => convert_to_unscaled_decimal::<Decimal64Type, Decimal64Type>(
610 d.integer(),
611 VariantDecimal8::MAX_PRECISION,
612 d.scale() as i8,
613 precision,
614 scale,
615 ),
616 Variant::Decimal16(d) => convert_to_unscaled_decimal::<Decimal128Type, Decimal64Type>(
617 d.integer(),
618 VariantDecimal16::MAX_PRECISION,
619 d.scale() as i8,
620 precision,
621 scale,
622 ),
623 _ => None,
624 }
625 }
626}
627
628impl ShredDecimalVariant for Decimal128Type {
629 fn shred_variant(value: &Variant<'_, '_>, precision: u8, scale: i8) -> Option<Self::Native> {
630 match *value {
631 Variant::Int8(i) => convert_to_unscaled_decimal::<Decimal128Type, Decimal128Type>(
632 i as i128,
633 VariantDecimal4::MAX_PRECISION,
634 0,
635 precision,
636 scale,
637 ),
638 Variant::Int16(i) => convert_to_unscaled_decimal::<Decimal128Type, Decimal128Type>(
639 i as i128,
640 VariantDecimal4::MAX_PRECISION,
641 0,
642 precision,
643 scale,
644 ),
645 Variant::Int32(i) => convert_to_unscaled_decimal::<Decimal128Type, Decimal128Type>(
646 i as i128,
647 VariantDecimal4::MAX_PRECISION,
648 0,
649 precision,
650 scale,
651 ),
652 Variant::Int64(i) => convert_to_unscaled_decimal::<Decimal128Type, Decimal128Type>(
653 i as i128,
654 VariantDecimal4::MAX_PRECISION,
655 0,
656 precision,
657 scale,
658 ),
659 Variant::Decimal4(d) => convert_to_unscaled_decimal::<Decimal32Type, Decimal128Type>(
660 d.integer(),
661 VariantDecimal4::MAX_PRECISION,
662 d.scale() as i8,
663 precision,
664 scale,
665 ),
666 Variant::Decimal8(d) => convert_to_unscaled_decimal::<Decimal64Type, Decimal128Type>(
667 d.integer(),
668 VariantDecimal8::MAX_PRECISION,
669 d.scale() as i8,
670 precision,
671 scale,
672 ),
673 Variant::Decimal16(d) => convert_to_unscaled_decimal::<Decimal128Type, Decimal128Type>(
674 d.integer(),
675 VariantDecimal16::MAX_PRECISION,
676 d.scale() as i8,
677 precision,
678 scale,
679 ),
680 _ => None,
681 }
682 }
683}
684
685impl ShredDecimalVariant for Decimal256Type {
686 fn shred_variant(_value: &Variant<'_, '_>, _precision: u8, _scale: i8) -> Option<Self::Native> {
687 None }
689}
690
691pub(crate) fn variant_to_boolean(variant: &Variant<'_, '_>, shred: bool) -> Option<bool> {
692 if shred {
693 return variant.as_boolean();
694 }
695
696 match variant {
697 Variant::BooleanTrue => Some(true),
698 Variant::BooleanFalse => Some(false),
699 Variant::Int8(i) => Some(cast_num_to_bool(*i)),
700 Variant::Int16(i) => Some(cast_num_to_bool(*i)),
701 Variant::Int32(i) => Some(cast_num_to_bool(*i)),
702 Variant::Int64(i) => Some(cast_num_to_bool(*i)),
703 Variant::Float(f) => Some(cast_num_to_bool(*f)),
704 Variant::Double(d) => Some(cast_num_to_bool(*d)),
705 Variant::ShortString(s) => cast_single_string_to_boolean_default(s.as_str()),
706 Variant::String(s) => cast_single_string_to_boolean_default(s),
707 _ => None,
708 }
709}
710
711macro_rules! non_generic_conversion_single_value {
713 ($array:expr, $cast_fn:expr, $index:expr) => {{
714 let array = $array;
715 if array.is_null($index) {
716 Ok(Variant::Null)
717 } else {
718 let cast_value = $cast_fn(array.value($index));
719 Ok(Variant::from(cast_value))
720 }
721 }};
722}
723pub(crate) use non_generic_conversion_single_value;
724
725macro_rules! generic_conversion_single_value {
729 ($t:ty, $method:ident, $cast_fn:expr, $input:expr, $index:expr) => {{
730 $crate::type_conversion::non_generic_conversion_single_value!(
731 $input.$method::<$t>(),
732 $cast_fn,
733 $index
734 )
735 }};
736}
737pub(crate) use generic_conversion_single_value;
738
739macro_rules! generic_conversion_single_value_with_result {
740 ($t:ty, $method:ident, $cast_fn:expr, $input:expr, $index:expr) => {{
741 let arr = $input.$method::<$t>();
742 let v = arr.value($index);
743 match ($cast_fn)(v) {
744 Ok(var) => Ok(Variant::from(var)),
745 Err(e) => Err(ArrowError::CastError(format!(
746 "Cast failed at index {idx} (array type: {ty}): {e}",
747 idx = $index,
748 ty = <$t as ::arrow::datatypes::ArrowPrimitiveType>::DATA_TYPE
749 ))),
750 }
751 }};
752}
753
754pub(crate) use generic_conversion_single_value_with_result;
755
756macro_rules! primitive_conversion_single_value {
758 ($t:ty, $input:expr, $index:expr) => {{
759 $crate::type_conversion::generic_conversion_single_value!(
760 $t,
761 as_primitive,
762 |v| v,
763 $input,
764 $index
765 )
766 }};
767}
768pub(crate) use primitive_conversion_single_value;