1use arrow_schema::ArrowError;
18use std::fmt;
19
20pub trait VariantDecimalType: Into<super::Variant<'static, 'static>> {
42 type Native;
44
45 const MAX_PRECISION: u8;
47 const MAX_UNSCALED_VALUE: Self::Native;
49
50 fn is_valid_precision_and_scale(precision: &u8, scale: &i8) -> bool {
69 (1..=Self::MAX_PRECISION).contains(precision) && (0..=*precision as i8).contains(scale)
70 }
71
72 fn try_new(integer: Self::Native, scale: u8) -> Result<Self, ArrowError>;
92
93 fn try_new_with_signed_scale(integer: Self::Native, scale: i8) -> Result<Self, ArrowError>;
101
102 fn integer(&self) -> Self::Native;
104
105 fn scale(&self) -> u8;
107
108 fn as_integer(&self) -> Option<Self::Native>;
113}
114
115macro_rules! impl_variant_decimal {
117 ($struct_name:ident, $native:ty) => {
118 impl $struct_name {
119 pub fn try_new(integer: $native, scale: u8) -> Result<Self, ArrowError> {
122 let max_precision = Self::MAX_PRECISION;
123 if scale > max_precision {
124 return Err(ArrowError::InvalidArgumentError(format!(
125 "Scale {scale} is larger than max precision {max_precision}",
126 )));
127 }
128 if !(-Self::MAX_UNSCALED_VALUE..=Self::MAX_UNSCALED_VALUE).contains(&integer) {
129 return Err(ArrowError::InvalidArgumentError(format!(
130 "{integer} is wider than max precision {max_precision}",
131 )));
132 }
133
134 Ok(Self { integer, scale })
135 }
136
137 pub fn integer(&self) -> $native {
141 self.integer
142 }
143
144 pub fn scale(&self) -> u8 {
148 self.scale
149 }
150
151 #[doc = concat!(
152 "Returns Some(`",
153 stringify!($native),
154 "`) if scale is zero or integer of the decimal is divisible by 10^scale,\n",
155 "None for other values.\n\n",
156 "",
157 "# Examples\n",
158 "```rust\n",
159 "use parquet_variant::", stringify!($struct_name), ";\n",
160 "//Return the integer if scale is 0\n",
161 "let d1 = ", stringify!($struct_name), "::try_new(123, 0).unwrap();\n",
162 "assert_eq!(d1.as_integer(), Some(123));\n",
163 "// or if the integer is divisible by 10^scale\n",
164 "let d2 = ", stringify!($struct_name), "::try_new(100, 2).unwrap();\n",
165 "assert_eq!(d2.as_integer(), Some(1));\n",
166 "// or the integer is negative and divisible by 10^scale\n",
167 "let d3 = ", stringify!($struct_name), "::try_new(-100, 2).unwrap();\n",
168 "assert_eq!(d3.as_integer(), Some(-1));\n",
169 "// or if the integer is 0\n",
170 "let d4 = ", stringify!($struct_name), "::try_new(0, 4).unwrap();\n",
171 "assert_eq!(d4.as_integer(), Some(0));\n",
172 "// but not if the integer is not divisible by 10^scale\n",
173 "let d5 = ", stringify!($struct_name), "::try_new(123, 2).unwrap();\n",
174 "assert_eq!(d5.as_integer(), None);\n",
175 "// or the integer is negative and not divisible by 10^scale\n",
176 "let d6 = ", stringify!($struct_name), "::try_new(-123, 2).unwrap();\n",
177 "assert_eq!(d6.as_integer(), None);\n",
178 "```\n",
179 )]
180 pub fn as_integer(&self) -> Option<$native> {
181 if self.scale == 0 {
182 return Some(self.integer);
183 }
184 let divisor = <$native>::pow(10, self.scale as u32);
185 (self.integer % divisor == 0).then(|| self.integer / divisor)
186 }
187 }
188
189 impl VariantDecimalType for $struct_name {
190 type Native = $native;
191 const MAX_PRECISION: u8 = Self::MAX_PRECISION;
192 const MAX_UNSCALED_VALUE: $native = <$native>::pow(10, Self::MAX_PRECISION as u32) - 1;
193
194 fn try_new(integer: $native, scale: u8) -> Result<Self, ArrowError> {
195 Self::try_new(integer, scale)
196 }
197
198 fn try_new_with_signed_scale(integer: $native, scale: i8) -> Result<Self, ArrowError> {
199 let (integer, scale) = if scale < 0 {
200 let multiplier = <$native>::checked_pow(10, -scale as u32);
201 let Some(rescaled) = multiplier.and_then(|m| integer.checked_mul(m)) else {
202 return Err(ArrowError::InvalidArgumentError(format!(
203 "Overflow when rescaling {integer} with scale {scale}"
204 )));
205 };
206 (rescaled, 0u8)
207 } else {
208 (integer, scale as u8)
209 };
210 Self::try_new(integer, scale)
211 }
212
213 fn integer(&self) -> $native {
214 self.integer()
215 }
216
217 fn scale(&self) -> u8 {
218 self.scale()
219 }
220
221 fn as_integer(&self) -> Option<$native> {
222 self.as_integer()
223 }
224 }
225
226 impl fmt::Display for $struct_name {
227 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
228 let integer = if self.scale == 0 {
229 self.integer
230 } else {
231 let divisor = <$native>::pow(10, self.scale as u32);
232 let remainder = self.integer % divisor;
233 if remainder != 0 {
234 let sign = if self.integer < 0 { "-" } else { "" };
236 let remainder =
238 format!("{:0width$}", remainder.abs(), width = self.scale as usize);
239 let remainder = remainder.trim_end_matches('0');
240 let quotient = (self.integer / divisor).abs();
241 return write!(f, "{sign}{quotient}.{remainder}");
242 }
243 self.integer / divisor
244 };
245 write!(f, "{integer}")
246 }
247 }
248 };
249}
250
251#[derive(Debug, Clone, Copy, PartialEq)]
266pub struct VariantDecimal4 {
267 integer: i32,
268 scale: u8,
269}
270
271impl VariantDecimal4 {
272 pub const MAX_PRECISION: u8 = arrow_schema::DECIMAL32_MAX_PRECISION;
274}
275
276impl_variant_decimal!(VariantDecimal4, i32);
277
278#[derive(Debug, Clone, Copy, PartialEq)]
294pub struct VariantDecimal8 {
295 integer: i64,
296 scale: u8,
297}
298
299impl VariantDecimal8 {
300 pub const MAX_PRECISION: u8 = arrow_schema::DECIMAL64_MAX_PRECISION;
302}
303
304impl_variant_decimal!(VariantDecimal8, i64);
305
306#[derive(Debug, Clone, Copy, PartialEq)]
322pub struct VariantDecimal16 {
323 integer: i128,
324 scale: u8,
325}
326
327impl VariantDecimal16 {
328 pub const MAX_PRECISION: u8 = arrow_schema::DECIMAL128_MAX_PRECISION;
330}
331
332impl_variant_decimal!(VariantDecimal16, i128);
333
334macro_rules! impl_from_decimal_for_decimal {
336 ($from_ty:ty, $for_ty:ty) => {
337 impl From<$from_ty> for $for_ty {
338 fn from(decimal: $from_ty) -> Self {
339 Self {
340 integer: decimal.integer.into(),
341 scale: decimal.scale,
342 }
343 }
344 }
345 };
346}
347
348impl_from_decimal_for_decimal!(VariantDecimal4, VariantDecimal8);
349impl_from_decimal_for_decimal!(VariantDecimal4, VariantDecimal16);
350impl_from_decimal_for_decimal!(VariantDecimal8, VariantDecimal16);
351
352macro_rules! impl_try_from_decimal_for_decimal {
354 ($from_ty:ty, $for_ty:ty) => {
355 impl TryFrom<$from_ty> for $for_ty {
356 type Error = ArrowError;
357
358 fn try_from(decimal: $from_ty) -> Result<Self, ArrowError> {
359 let Ok(integer) = decimal.integer.try_into() else {
360 return Err(ArrowError::InvalidArgumentError(format!(
361 "Value {} is wider than max precision {}",
362 decimal.integer,
363 Self::MAX_PRECISION
364 )));
365 };
366 Self::try_new(integer, decimal.scale)
367 }
368 }
369 };
370}
371
372impl_try_from_decimal_for_decimal!(VariantDecimal8, VariantDecimal4);
373impl_try_from_decimal_for_decimal!(VariantDecimal16, VariantDecimal4);
374impl_try_from_decimal_for_decimal!(VariantDecimal16, VariantDecimal8);
375
376macro_rules! impl_try_from_int_for_decimal {
378 ($from_ty:ty, $for_ty:ty) => {
379 impl TryFrom<$from_ty> for $for_ty {
380 type Error = ArrowError;
381
382 fn try_from(integer: $from_ty) -> Result<Self, ArrowError> {
383 Self::try_new(integer, 0)
384 }
385 }
386 };
387}
388
389impl_try_from_int_for_decimal!(i32, VariantDecimal4);
390impl_try_from_int_for_decimal!(i64, VariantDecimal8);
391impl_try_from_int_for_decimal!(i128, VariantDecimal16);
392
393#[cfg(test)]
394mod tests {
395 use super::*;
396
397 #[test]
398 fn test_variant_decimal_invalid_precision() {
399 let decimal4_too_large = VariantDecimal4::try_new(1_000_000_000_i32, 2);
401 assert!(
402 decimal4_too_large.is_err(),
403 "Decimal4 precision overflow should fail"
404 );
405 assert!(
406 decimal4_too_large
407 .unwrap_err()
408 .to_string()
409 .contains("wider than max precision")
410 );
411
412 let decimal4_too_small = VariantDecimal4::try_new(-1_000_000_000_i32, 2);
413 assert!(
414 decimal4_too_small.is_err(),
415 "Decimal4 precision underflow should fail"
416 );
417 assert!(
418 decimal4_too_small
419 .unwrap_err()
420 .to_string()
421 .contains("wider than max precision")
422 );
423
424 let decimal4_max_valid = VariantDecimal4::try_new(999_999_999_i32, 2);
426 assert!(
427 decimal4_max_valid.is_ok(),
428 "Decimal4 max valid value should succeed"
429 );
430
431 let decimal4_min_valid = VariantDecimal4::try_new(-999_999_999_i32, 2);
432 assert!(
433 decimal4_min_valid.is_ok(),
434 "Decimal4 min valid value should succeed"
435 );
436
437 let decimal8_too_large = VariantDecimal8::try_new(1_000_000_000_000_000_000_i64, 2);
439 assert!(
440 decimal8_too_large.is_err(),
441 "Decimal8 precision overflow should fail"
442 );
443 assert!(
444 decimal8_too_large
445 .unwrap_err()
446 .to_string()
447 .contains("wider than max precision")
448 );
449
450 let decimal8_too_small = VariantDecimal8::try_new(-1_000_000_000_000_000_000_i64, 2);
451 assert!(
452 decimal8_too_small.is_err(),
453 "Decimal8 precision underflow should fail"
454 );
455 assert!(
456 decimal8_too_small
457 .unwrap_err()
458 .to_string()
459 .contains("wider than max precision")
460 );
461
462 let decimal8_max_valid = VariantDecimal8::try_new(999_999_999_999_999_999_i64, 2);
464 assert!(
465 decimal8_max_valid.is_ok(),
466 "Decimal8 max valid value should succeed"
467 );
468
469 let decimal8_min_valid = VariantDecimal8::try_new(-999_999_999_999_999_999_i64, 2);
470 assert!(
471 decimal8_min_valid.is_ok(),
472 "Decimal8 min valid value should succeed"
473 );
474
475 let decimal16_too_large =
477 VariantDecimal16::try_new(100000000000000000000000000000000000000_i128, 2);
478 assert!(
479 decimal16_too_large.is_err(),
480 "Decimal16 precision overflow should fail"
481 );
482 assert!(
483 decimal16_too_large
484 .unwrap_err()
485 .to_string()
486 .contains("wider than max precision")
487 );
488
489 let decimal16_too_small =
490 VariantDecimal16::try_new(-100000000000000000000000000000000000000_i128, 2);
491 assert!(
492 decimal16_too_small.is_err(),
493 "Decimal16 precision underflow should fail"
494 );
495 assert!(
496 decimal16_too_small
497 .unwrap_err()
498 .to_string()
499 .contains("wider than max precision")
500 );
501
502 let decimal16_max_valid =
504 VariantDecimal16::try_new(99999999999999999999999999999999999999_i128, 2);
505 assert!(
506 decimal16_max_valid.is_ok(),
507 "Decimal16 max valid value should succeed"
508 );
509
510 let decimal16_min_valid =
511 VariantDecimal16::try_new(-99999999999999999999999999999999999999_i128, 2);
512 assert!(
513 decimal16_min_valid.is_ok(),
514 "Decimal16 min valid value should succeed"
515 );
516 }
517
518 #[test]
519 fn test_variant_decimal_invalid_scale() {
520 let decimal4_invalid_scale = VariantDecimal4::try_new(123_i32, 10);
522 assert!(
523 decimal4_invalid_scale.is_err(),
524 "Decimal4 with scale > 9 should fail"
525 );
526 assert!(
527 decimal4_invalid_scale
528 .unwrap_err()
529 .to_string()
530 .contains("larger than max precision")
531 );
532
533 let decimal4_invalid_scale_large = VariantDecimal4::try_new(123_i32, 20);
534 assert!(
535 decimal4_invalid_scale_large.is_err(),
536 "Decimal4 with scale > 9 should fail"
537 );
538
539 let decimal4_valid_scale = VariantDecimal4::try_new(123_i32, 9);
541 assert!(
542 decimal4_valid_scale.is_ok(),
543 "Decimal4 with scale = 9 should succeed"
544 );
545
546 let decimal8_invalid_scale = VariantDecimal8::try_new(123_i64, 19);
548 assert!(
549 decimal8_invalid_scale.is_err(),
550 "Decimal8 with scale > 18 should fail"
551 );
552 assert!(
553 decimal8_invalid_scale
554 .unwrap_err()
555 .to_string()
556 .contains("larger than max precision")
557 );
558
559 let decimal8_invalid_scale_large = VariantDecimal8::try_new(123_i64, 25);
560 assert!(
561 decimal8_invalid_scale_large.is_err(),
562 "Decimal8 with scale > 18 should fail"
563 );
564
565 let decimal8_valid_scale = VariantDecimal8::try_new(123_i64, 18);
567 assert!(
568 decimal8_valid_scale.is_ok(),
569 "Decimal8 with scale = 18 should succeed"
570 );
571
572 let decimal16_invalid_scale = VariantDecimal16::try_new(123_i128, 39);
574 assert!(
575 decimal16_invalid_scale.is_err(),
576 "Decimal16 with scale > 38 should fail"
577 );
578 assert!(
579 decimal16_invalid_scale
580 .unwrap_err()
581 .to_string()
582 .contains("larger than max precision")
583 );
584
585 let decimal16_invalid_scale_large = VariantDecimal16::try_new(123_i128, 50);
586 assert!(
587 decimal16_invalid_scale_large.is_err(),
588 "Decimal16 with scale > 38 should fail"
589 );
590
591 let decimal16_valid_scale = VariantDecimal16::try_new(123_i128, 38);
593 assert!(
594 decimal16_valid_scale.is_ok(),
595 "Decimal16 with scale = 38 should succeed"
596 );
597 }
598
599 #[test]
600 fn test_variant_decimal4_display() {
601 let d = VariantDecimal4::try_new(42, 0).unwrap();
603 assert_eq!(d.to_string(), "42");
604
605 let d = VariantDecimal4::try_new(-42, 0).unwrap();
606 assert_eq!(d.to_string(), "-42");
607
608 let d = VariantDecimal4::try_new(12345, 2).unwrap();
610 assert_eq!(d.to_string(), "123.45");
611
612 let d = VariantDecimal4::try_new(-12345, 2).unwrap();
613 assert_eq!(d.to_string(), "-123.45");
614
615 let d = VariantDecimal4::try_new(12300, 2).unwrap();
617 assert_eq!(d.to_string(), "123");
618
619 let d = VariantDecimal4::try_new(-12300, 2).unwrap();
620 assert_eq!(d.to_string(), "-123");
621
622 let d = VariantDecimal4::try_new(1005, 3).unwrap();
624 assert_eq!(d.to_string(), "1.005");
625
626 let d = VariantDecimal4::try_new(-1005, 3).unwrap();
627 assert_eq!(d.to_string(), "-1.005");
628
629 let d = VariantDecimal4::try_new(123, 4).unwrap();
631 assert_eq!(d.to_string(), "0.0123");
632
633 let d = VariantDecimal4::try_new(-123, 4).unwrap();
634 assert_eq!(d.to_string(), "-0.0123");
635
636 let d = VariantDecimal4::try_new(0, 0).unwrap();
638 assert_eq!(d.to_string(), "0");
639
640 let d = VariantDecimal4::try_new(0, 3).unwrap();
641 assert_eq!(d.to_string(), "0");
642
643 let d = VariantDecimal4::try_new(123456789, 9).unwrap();
645 assert_eq!(d.to_string(), "0.123456789");
646
647 let d = VariantDecimal4::try_new(-123456789, 9).unwrap();
648 assert_eq!(d.to_string(), "-0.123456789");
649
650 let d = VariantDecimal4::try_new(999999999, 0).unwrap();
652 assert_eq!(d.to_string(), "999999999");
653
654 let d = VariantDecimal4::try_new(-999999999, 0).unwrap();
655 assert_eq!(d.to_string(), "-999999999");
656
657 let d = VariantDecimal4::try_new(120050, 4).unwrap();
659 assert_eq!(d.to_string(), "12.005");
660
661 let d = VariantDecimal4::try_new(-120050, 4).unwrap();
662 assert_eq!(d.to_string(), "-12.005");
663 }
664
665 #[test]
666 fn test_variant_decimal8_display() {
667 let d = VariantDecimal8::try_new(42, 0).unwrap();
669 assert_eq!(d.to_string(), "42");
670
671 let d = VariantDecimal8::try_new(-42, 0).unwrap();
672 assert_eq!(d.to_string(), "-42");
673
674 let d = VariantDecimal8::try_new(1234567890, 3).unwrap();
676 assert_eq!(d.to_string(), "1234567.89");
677
678 let d = VariantDecimal8::try_new(-1234567890, 3).unwrap();
679 assert_eq!(d.to_string(), "-1234567.89");
680
681 let d = VariantDecimal8::try_new(123000000, 6).unwrap();
683 assert_eq!(d.to_string(), "123");
684
685 let d = VariantDecimal8::try_new(-123000000, 6).unwrap();
686 assert_eq!(d.to_string(), "-123");
687
688 let d = VariantDecimal8::try_new(100005, 6).unwrap();
690 assert_eq!(d.to_string(), "0.100005");
691
692 let d = VariantDecimal8::try_new(-100005, 6).unwrap();
693 assert_eq!(d.to_string(), "-0.100005");
694
695 let d = VariantDecimal8::try_new(123, 10).unwrap();
697 assert_eq!(d.to_string(), "0.0000000123");
698
699 let d = VariantDecimal8::try_new(-123, 10).unwrap();
700 assert_eq!(d.to_string(), "-0.0000000123");
701
702 let d = VariantDecimal8::try_new(0, 0).unwrap();
704 assert_eq!(d.to_string(), "0");
705
706 let d = VariantDecimal8::try_new(0, 10).unwrap();
707 assert_eq!(d.to_string(), "0");
708
709 let d = VariantDecimal8::try_new(123456789012345678, 18).unwrap();
711 assert_eq!(d.to_string(), "0.123456789012345678");
712
713 let d = VariantDecimal8::try_new(-123456789012345678, 18).unwrap();
714 assert_eq!(d.to_string(), "-0.123456789012345678");
715
716 let d = VariantDecimal8::try_new(999999999999999999, 0).unwrap();
718 assert_eq!(d.to_string(), "999999999999999999");
719
720 let d = VariantDecimal8::try_new(-999999999999999999, 0).unwrap();
721 assert_eq!(d.to_string(), "-999999999999999999");
722
723 let d = VariantDecimal8::try_new(1200000050000, 10).unwrap();
725 assert_eq!(d.to_string(), "120.000005");
726
727 let d = VariantDecimal8::try_new(-1200000050000, 10).unwrap();
728 assert_eq!(d.to_string(), "-120.000005");
729 }
730
731 #[test]
732 fn test_variant_decimal16_display() {
733 let d = VariantDecimal16::try_new(42, 0).unwrap();
735 assert_eq!(d.to_string(), "42");
736
737 let d = VariantDecimal16::try_new(-42, 0).unwrap();
738 assert_eq!(d.to_string(), "-42");
739
740 let d = VariantDecimal16::try_new(123456789012345, 4).unwrap();
742 assert_eq!(d.to_string(), "12345678901.2345");
743
744 let d = VariantDecimal16::try_new(-123456789012345, 4).unwrap();
745 assert_eq!(d.to_string(), "-12345678901.2345");
746
747 let d = VariantDecimal16::try_new(12300000000, 8).unwrap();
749 assert_eq!(d.to_string(), "123");
750
751 let d = VariantDecimal16::try_new(-12300000000, 8).unwrap();
752 assert_eq!(d.to_string(), "-123");
753
754 let d = VariantDecimal16::try_new(10000005, 8).unwrap();
756 assert_eq!(d.to_string(), "0.10000005");
757
758 let d = VariantDecimal16::try_new(-10000005, 8).unwrap();
759 assert_eq!(d.to_string(), "-0.10000005");
760
761 let d = VariantDecimal16::try_new(123, 20).unwrap();
763 assert_eq!(d.to_string(), "0.00000000000000000123");
764
765 let d = VariantDecimal16::try_new(-123, 20).unwrap();
766 assert_eq!(d.to_string(), "-0.00000000000000000123");
767
768 let d = VariantDecimal16::try_new(0, 0).unwrap();
770 assert_eq!(d.to_string(), "0");
771
772 let d = VariantDecimal16::try_new(0, 20).unwrap();
773 assert_eq!(d.to_string(), "0");
774
775 let d = VariantDecimal16::try_new(12345678901234567890123456789012345678_i128, 38).unwrap();
777 assert_eq!(d.to_string(), "0.12345678901234567890123456789012345678");
778
779 let d =
780 VariantDecimal16::try_new(-12345678901234567890123456789012345678_i128, 38).unwrap();
781 assert_eq!(d.to_string(), "-0.12345678901234567890123456789012345678");
782
783 let d = VariantDecimal16::try_new(99999999999999999999999999999999999999_i128, 0).unwrap();
785 assert_eq!(d.to_string(), "99999999999999999999999999999999999999");
786
787 let d = VariantDecimal16::try_new(-99999999999999999999999999999999999999_i128, 0).unwrap();
788 assert_eq!(d.to_string(), "-99999999999999999999999999999999999999");
789
790 let d = VariantDecimal16::try_new(12000000000000050000000000000_i128, 25).unwrap();
792 assert_eq!(d.to_string(), "1200.000000000005");
793
794 let d = VariantDecimal16::try_new(-12000000000000050000000000000_i128, 25).unwrap();
795 assert_eq!(d.to_string(), "-1200.000000000005");
796
797 let large_int = 12345678901234567890123456789_i128;
799 let d = VariantDecimal16::try_new(large_int, 0).unwrap();
800 assert_eq!(d.to_string(), "12345678901234567890123456789");
801
802 let d = VariantDecimal16::try_new(-large_int, 0).unwrap();
803 assert_eq!(d.to_string(), "-12345678901234567890123456789");
804 }
805}