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