Skip to main content

decimal_bytes/
lib.rs

1//! # decimal-bytes
2//!
3//! Arbitrary precision decimals with lexicographically sortable byte encoding.
4//!
5//! This crate provides three decimal types optimized for database storage:
6//!
7//! - **[`Decimal`]**: Variable-length arbitrary precision (up to 131,072 digits)
8//! - **[`Decimal64`]**: Fixed 8-byte representation with embedded scale (precision ≤ 16 digits)
9//! - **[`Decimal64NoScale`]**: Fixed 8-byte representation with external scale (precision ≤ 18 digits)
10//!
11//! All types support PostgreSQL special values (NaN, ±Infinity) with correct sort ordering.
12//!
13//! ## When to Use Which
14//!
15//! | Type | Precision | Scale | Storage | Best For |
16//! |------|-----------|-------|---------|----------|
17//! | `Decimal64NoScale` | ≤ 18 digits | External | 8 bytes | **Columnar storage**, aggregates |
18//! | `Decimal64` | ≤ 16 digits | Embedded | 8 bytes | Self-contained values |
19//! | `Decimal` | Unlimited | Unlimited | Variable | Scientific, very large numbers |
20//!
21//! ## Decimal64NoScale (Recommended for Columnar Storage)
22//!
23//! ```
24//! use decimal_bytes::Decimal64NoScale;
25//!
26//! // Scale is provided externally (e.g., from schema metadata)
27//! let scale = 2;
28//! let a = Decimal64NoScale::new("100.50", scale).unwrap();
29//! let b = Decimal64NoScale::new("200.25", scale).unwrap();
30//!
31//! // Aggregates work correctly - just sum the raw i64 values!
32//! let sum = a.value() + b.value();  // 30075
33//! let result = Decimal64NoScale::from_raw(sum);
34//! assert_eq!(result.to_string_with_scale(scale), "300.75");
35//! ```
36//!
37//! ## Decimal64 Example
38//!
39//! ```
40//! use decimal_bytes::Decimal64;
41//!
42//! // Create with embedded scale
43//! let price = Decimal64::new("99.99", 2).unwrap();
44//! assert_eq!(price.to_string(), "99.99");
45//! assert_eq!(price.scale(), 2);  // Scale is embedded!
46//!
47//! // With precision and scale (PostgreSQL NUMERIC semantics)
48//! let d = Decimal64::with_precision_scale("123.456", Some(5), Some(2)).unwrap();
49//! assert_eq!(d.to_string(), "123.46"); // Rounded
50//!
51//! // Parse with automatic scale detection
52//! let d: Decimal64 = "123.456".parse().unwrap();
53//! assert_eq!(d.scale(), 3);
54//!
55//! // Special values
56//! let inf = Decimal64::infinity();
57//! let nan = Decimal64::nan();
58//! assert!(price < inf);
59//! assert!(inf < nan);
60//! ```
61//!
62//! ## Decimal Example (Arbitrary Precision)
63//!
64//! ```
65//! use decimal_bytes::Decimal;
66//! use std::str::FromStr;
67//!
68//! // Create decimals
69//! let a = Decimal::from_str("123.456").unwrap();
70//! let b = Decimal::from_str("123.457").unwrap();
71//!
72//! // Byte comparison matches numerical comparison
73//! assert!(a.as_bytes() < b.as_bytes());
74//! assert!(a < b);
75//!
76//! // Special values (PostgreSQL compatible)
77//! let inf = Decimal::infinity();
78//! let nan = Decimal::nan();
79//! assert!(a < inf);
80//! assert!(inf < nan);
81//! ```
82//!
83//! ## Sort Order
84//!
85//! The lexicographic byte order matches PostgreSQL NUMERIC:
86//!
87//! ```text
88//! -Infinity < negative numbers < zero < positive numbers < +Infinity < NaN
89//! ```
90//!
91//! Both `Decimal` and `Decimal64` support this sort order including special values.
92//!
93//! ## Special Value Semantics (PostgreSQL vs IEEE 754)
94//!
95//! The `Decimal` type follows **PostgreSQL semantics** for special values:
96//!
97//! | Behavior | PostgreSQL / decimal-bytes | IEEE 754 float |
98//! |----------|---------------------------|----------------|
99//! | `NaN == NaN` | `true` | `false` |
100//! | `NaN` ordering | Greatest value (> Infinity) | Unordered |
101//! | `Infinity == Infinity` | `true` | `true` |
102//!
103//! ```
104//! use decimal_bytes::Decimal;
105//!
106//! let nan1 = Decimal::nan();
107//! let nan2 = Decimal::nan();
108//! let inf = Decimal::infinity();
109//!
110//! // NaN equals itself (PostgreSQL behavior, unlike IEEE 754)
111//! assert_eq!(nan1, nan2);
112//!
113//! // NaN is greater than everything, including Infinity
114//! assert!(nan1 > inf);
115//! ```
116//!
117//! This makes `Decimal` suitable for use in indexes, sorting, and deduplication
118//! where consistent ordering and equality semantics are required.
119
120mod decimal64;
121mod decimal64_no_scale;
122mod encoding;
123
124use std::cmp::Ordering;
125use std::fmt;
126use std::hash::{Hash, Hasher};
127use std::str::FromStr;
128
129use serde::{Deserialize, Deserializer, Serialize, Serializer};
130
131pub use decimal64::{Decimal64, MAX_DECIMAL64_PRECISION, MAX_DECIMAL64_SCALE};
132pub use decimal64_no_scale::{
133    Decimal64NoScale, MAX_DECIMAL64_NO_SCALE_PRECISION, MAX_DECIMAL64_NO_SCALE_SCALE,
134};
135pub use encoding::DecimalError;
136pub use encoding::SpecialValue;
137use encoding::{
138    decode_special_value, decode_to_string, decode_to_string_with_scale, encode_decimal,
139    encode_decimal_with_constraints, encode_special_value, ENCODING_NAN, ENCODING_NEG_INFINITY,
140    ENCODING_POS_INFINITY,
141};
142
143/// An arbitrary precision decimal number stored as sortable bytes.
144///
145/// The internal byte representation is designed to be lexicographically sortable,
146/// meaning that comparing the bytes directly yields the same result as comparing
147/// the numerical values. This enables efficient range queries in databases.
148///
149/// # Storage Efficiency
150///
151/// The encoding uses:
152/// - 1 byte for the sign
153/// - Variable bytes for the exponent (typically 1-3 bytes)
154/// - 4 bits per decimal digit (BCD encoding, 2 digits per byte)
155#[derive(Clone)]
156pub struct Decimal {
157    bytes: Vec<u8>,
158}
159
160impl Decimal {
161    /// Creates a new Decimal with precision and scale constraints.
162    ///
163    /// Values that exceed the constraints are truncated/rounded to fit.
164    /// This is compatible with SQL NUMERIC(precision, scale) semantics.
165    ///
166    /// - `precision`: Maximum total number of significant digits (None = unlimited)
167    /// - `scale`: Digits after decimal point; negative values round to left of decimal
168    ///
169    /// # PostgreSQL Compatibility
170    ///
171    /// Supports negative scale (rounds to powers of 10):
172    /// - `scale = -3` rounds to nearest 1000
173    /// - `NUMERIC(2, -3)` allows values like -99000 to 99000
174    ///
175    /// # Examples
176    ///
177    /// ```
178    /// use decimal_bytes::Decimal;
179    ///
180    /// // NUMERIC(5, 2) - up to 5 digits total, 2 after decimal
181    /// let d = Decimal::with_precision_scale("123.456", Some(5), Some(2)).unwrap();
182    /// assert_eq!(d.to_string(), "123.46"); // Rounded to 2 decimal places
183    ///
184    /// // NUMERIC(2, -3) - rounds to nearest 1000, max 2 significant digits
185    /// let d = Decimal::with_precision_scale("12345", Some(2), Some(-3)).unwrap();
186    /// assert_eq!(d.to_string(), "12000"); // Rounded to nearest 1000
187    /// ```
188    pub fn with_precision_scale(
189        s: &str,
190        precision: Option<u32>,
191        scale: Option<i32>,
192    ) -> Result<Self, DecimalError> {
193        let bytes = encode_decimal_with_constraints(s, precision, scale)?;
194        Ok(Self { bytes })
195    }
196
197    /// Creates a Decimal from raw bytes.
198    ///
199    /// The bytes must be a valid encoding produced by `as_bytes()`.
200    /// Returns an error if the bytes are invalid.
201    ///
202    /// # Examples
203    ///
204    /// ```
205    /// use decimal_bytes::Decimal;
206    /// use std::str::FromStr;
207    ///
208    /// let original = Decimal::from_str("123.456").unwrap();
209    /// let bytes = original.as_bytes();
210    /// let restored = Decimal::from_bytes(bytes).unwrap();
211    /// assert_eq!(original, restored);
212    /// ```
213    pub fn from_bytes(bytes: &[u8]) -> Result<Self, DecimalError> {
214        // Validate by attempting to decode
215        let _ = decode_to_string(bytes)?;
216        Ok(Self {
217            bytes: bytes.to_vec(),
218        })
219    }
220
221    /// Creates a Decimal from raw bytes without validation.
222    ///
223    /// # Safety
224    ///
225    /// The caller must ensure the bytes are a valid encoding.
226    /// Using invalid bytes may cause panics or incorrect results.
227    #[inline]
228    pub fn from_bytes_unchecked(bytes: Vec<u8>) -> Self {
229        Self { bytes }
230    }
231
232    /// Returns the raw byte representation.
233    ///
234    /// These bytes are lexicographically sortable - comparing them directly
235    /// yields the same result as comparing the numerical values.
236    #[inline]
237    pub fn as_bytes(&self) -> &[u8] {
238        &self.bytes
239    }
240
241    /// Consumes the Decimal and returns the underlying bytes.
242    #[inline]
243    pub fn into_bytes(self) -> Vec<u8> {
244        self.bytes
245    }
246
247    /// Returns true if this decimal represents zero.
248    pub fn is_zero(&self) -> bool {
249        self.bytes.len() == 1 && self.bytes[0] == encoding::SIGN_ZERO
250    }
251
252    /// Returns true if this decimal is negative.
253    pub fn is_negative(&self) -> bool {
254        !self.bytes.is_empty() && self.bytes[0] == encoding::SIGN_NEGATIVE
255    }
256
257    /// Returns true if this decimal is positive (and not zero).
258    pub fn is_positive(&self) -> bool {
259        !self.bytes.is_empty() && self.bytes[0] == encoding::SIGN_POSITIVE
260    }
261
262    /// Returns true if this decimal represents positive infinity.
263    pub fn is_pos_infinity(&self) -> bool {
264        self.bytes.as_slice() == ENCODING_POS_INFINITY
265    }
266
267    /// Returns true if this decimal represents negative infinity.
268    pub fn is_neg_infinity(&self) -> bool {
269        self.bytes.as_slice() == ENCODING_NEG_INFINITY
270    }
271
272    /// Returns true if this decimal represents positive or negative infinity.
273    pub fn is_infinity(&self) -> bool {
274        self.is_pos_infinity() || self.is_neg_infinity()
275    }
276
277    /// Returns true if this decimal represents NaN (Not a Number).
278    pub fn is_nan(&self) -> bool {
279        self.bytes.as_slice() == ENCODING_NAN
280    }
281
282    /// Returns true if this decimal is a special value (Infinity or NaN).
283    pub fn is_special(&self) -> bool {
284        decode_special_value(&self.bytes).is_some()
285    }
286
287    /// Returns true if this decimal is a finite number (not Infinity or NaN).
288    pub fn is_finite(&self) -> bool {
289        !self.is_special()
290    }
291
292    /// Returns the number of bytes used to store this decimal.
293    #[inline]
294    pub fn byte_len(&self) -> usize {
295        self.bytes.len()
296    }
297
298    /// Creates positive infinity.
299    ///
300    /// Infinity is greater than all finite numbers but less than NaN.
301    /// Two positive infinities are equal to each other.
302    ///
303    /// # Example
304    ///
305    /// ```
306    /// use decimal_bytes::Decimal;
307    /// use std::str::FromStr;
308    ///
309    /// let inf = Decimal::infinity();
310    /// let big = Decimal::from_str("999999999999").unwrap();
311    /// assert!(inf > big);
312    /// assert_eq!(inf, Decimal::infinity());
313    /// ```
314    pub fn infinity() -> Self {
315        Self {
316            bytes: encode_special_value(SpecialValue::Infinity),
317        }
318    }
319
320    /// Creates negative infinity.
321    ///
322    /// Negative infinity is less than all finite numbers and positive infinity.
323    /// Two negative infinities are equal to each other.
324    ///
325    /// # Example
326    ///
327    /// ```
328    /// use decimal_bytes::Decimal;
329    /// use std::str::FromStr;
330    ///
331    /// let neg_inf = Decimal::neg_infinity();
332    /// let small = Decimal::from_str("-999999999999").unwrap();
333    /// assert!(neg_inf < small);
334    /// assert_eq!(neg_inf, Decimal::neg_infinity());
335    /// ```
336    pub fn neg_infinity() -> Self {
337        Self {
338            bytes: encode_special_value(SpecialValue::NegInfinity),
339        }
340    }
341
342    /// Creates NaN (Not a Number).
343    ///
344    /// # PostgreSQL Semantics
345    ///
346    /// Unlike IEEE 754 floating-point where `NaN != NaN`, this follows PostgreSQL
347    /// semantics where:
348    /// - `NaN == NaN` is `true`
349    /// - `NaN` is the greatest value (greater than positive infinity)
350    /// - All NaN values are equal regardless of how they were created
351    ///
352    /// This makes NaN usable in sorting, indexing, and deduplication.
353    ///
354    /// # Example
355    ///
356    /// ```
357    /// use decimal_bytes::Decimal;
358    /// use std::str::FromStr;
359    ///
360    /// let nan1 = Decimal::nan();
361    /// let nan2 = Decimal::from_str("NaN").unwrap();
362    /// let inf = Decimal::infinity();
363    ///
364    /// // NaN equals itself (PostgreSQL behavior)
365    /// assert_eq!(nan1, nan2);
366    ///
367    /// // NaN is greater than everything
368    /// assert!(nan1 > inf);
369    /// ```
370    pub fn nan() -> Self {
371        Self {
372            bytes: encode_special_value(SpecialValue::NaN),
373        }
374    }
375
376    /// Converts this decimal to a string with a specific scale (number of decimal places).
377    ///
378    /// This ensures the output has exactly `scale` decimal places, adding trailing
379    /// zeros if needed. Useful for PostgreSQL NUMERIC display formatting where
380    /// the scale defines the display format.
381    ///
382    /// - Positive scale: adds decimal point with trailing zeros as needed
383    /// - Zero scale: no decimal point added
384    /// - Negative scale: no decimal point (value is already an integer)
385    ///
386    /// Special values (NaN, Infinity, -Infinity) are returned as-is without scale formatting.
387    ///
388    /// # Arguments
389    /// * `scale` - Number of decimal places to ensure in the output
390    ///
391    /// # Examples
392    ///
393    /// ```
394    /// use decimal_bytes::Decimal;
395    /// use std::str::FromStr;
396    ///
397    /// // Positive scale: includes decimal point and trailing zeros
398    /// let d = Decimal::from_str("1").unwrap();
399    /// assert_eq!(d.to_string_with_scale(18), "1.000000000000000000");
400    ///
401    /// let d = Decimal::from_str("1.5").unwrap();
402    /// assert_eq!(d.to_string_with_scale(3), "1.500");
403    ///
404    /// // Zero scale: no decimal point
405    /// let d = Decimal::from_str("123").unwrap();
406    /// assert_eq!(d.to_string_with_scale(0), "123");
407    ///
408    /// // Negative scale: no decimal point
409    /// let d = Decimal::from_str("100").unwrap();
410    /// assert_eq!(d.to_string_with_scale(-2), "100");
411    ///
412    /// // Special values are unchanged
413    /// let nan = Decimal::nan();
414    /// assert_eq!(nan.to_string_with_scale(10), "NaN");
415    /// ```
416    pub fn to_string_with_scale(&self, scale: i32) -> String {
417        decode_to_string_with_scale(&self.bytes, scale).expect("Decimal contains valid bytes")
418    }
419}
420
421impl FromStr for Decimal {
422    type Err = DecimalError;
423
424    /// Creates a new Decimal from a string representation.
425    ///
426    /// # Examples
427    ///
428    /// ```
429    /// use decimal_bytes::Decimal;
430    /// use std::str::FromStr;
431    ///
432    /// let d = Decimal::from_str("123.456").unwrap();
433    /// let d = Decimal::from_str("-0.001").unwrap();
434    /// let d = Decimal::from_str("1e10").unwrap();
435    /// // Or use parse:
436    /// let d: Decimal = "42.5".parse().unwrap();
437    /// ```
438    fn from_str(s: &str) -> Result<Self, Self::Err> {
439        let bytes = encode_decimal(s)?;
440        Ok(Self { bytes })
441    }
442}
443
444impl fmt::Display for Decimal {
445    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
446        let s = decode_to_string(&self.bytes).expect("Decimal contains valid bytes");
447        write!(f, "{}", s)
448    }
449}
450
451impl fmt::Debug for Decimal {
452    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
453        let value = decode_to_string(&self.bytes).expect("Decimal contains valid bytes");
454        f.debug_struct("Decimal")
455            .field("value", &value)
456            .field("bytes", &self.bytes)
457            .finish()
458    }
459}
460
461impl PartialEq for Decimal {
462    fn eq(&self, other: &Self) -> bool {
463        self.bytes == other.bytes
464    }
465}
466
467impl Eq for Decimal {}
468
469impl PartialOrd for Decimal {
470    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
471        Some(self.cmp(other))
472    }
473}
474
475impl Ord for Decimal {
476    fn cmp(&self, other: &Self) -> Ordering {
477        // Byte comparison is equivalent to numerical comparison
478        self.bytes.cmp(&other.bytes)
479    }
480}
481
482impl Hash for Decimal {
483    fn hash<H: Hasher>(&self, state: &mut H) {
484        self.bytes.hash(state);
485    }
486}
487
488impl Serialize for Decimal {
489    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
490    where
491        S: Serializer,
492    {
493        // Serialize as string for human readability in JSON
494        serializer.serialize_str(&self.to_string())
495    }
496}
497
498impl<'de> Deserialize<'de> for Decimal {
499    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
500    where
501        D: Deserializer<'de>,
502    {
503        let s = String::deserialize(deserializer)?;
504        Decimal::from_str(&s).map_err(serde::de::Error::custom)
505    }
506}
507
508// Conversion from integer types
509macro_rules! impl_from_int {
510    ($($t:ty),*) => {
511        $(
512            impl From<$t> for Decimal {
513                fn from(val: $t) -> Self {
514                    // Integer conversion is infallible - always produces valid decimal bytes.
515                    // Uses string conversion internally since the byte encoding is designed
516                    // around decimal string parsing (BCD encoding of digit pairs).
517                    Decimal::from_str(&val.to_string()).expect("Integer is always valid")
518                }
519            }
520        )*
521    };
522}
523
524impl_from_int!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128);
525
526impl TryFrom<f64> for Decimal {
527    type Error = DecimalError;
528
529    /// Converts an f64 to a Decimal.
530    ///
531    /// Special values are handled:
532    /// - `f64::NAN` → `Decimal::nan()`
533    /// - `f64::INFINITY` → `Decimal::infinity()`
534    /// - `f64::NEG_INFINITY` → `Decimal::neg_infinity()`
535    ///
536    /// Note: Due to f64's limited precision (~15-17 significant digits),
537    /// very precise decimal values may lose precision when converted from f64.
538    /// For exact decimal representation, use `Decimal::from_str()` instead.
539    ///
540    /// # Example
541    ///
542    /// ```
543    /// use decimal_bytes::Decimal;
544    ///
545    /// let d: Decimal = 123.456f64.try_into().unwrap();
546    /// assert_eq!(d.to_string(), "123.456");
547    ///
548    /// let inf: Decimal = f64::INFINITY.try_into().unwrap();
549    /// assert!(inf.is_pos_infinity());
550    ///
551    /// let nan: Decimal = f64::NAN.try_into().unwrap();
552    /// assert!(nan.is_nan());
553    /// ```
554    fn try_from(val: f64) -> Result<Self, Self::Error> {
555        if val.is_nan() {
556            return Ok(Decimal::nan());
557        }
558        if val.is_infinite() {
559            return Ok(if val.is_sign_positive() {
560                Decimal::infinity()
561            } else {
562                Decimal::neg_infinity()
563            });
564        }
565        Decimal::from_str(&val.to_string())
566    }
567}
568
569impl Default for Decimal {
570    fn default() -> Self {
571        Decimal {
572            bytes: vec![encoding::SIGN_ZERO],
573        }
574    }
575}
576
577// ============================================================================
578// Optional: rust_decimal interop (enabled with "rust_decimal" feature)
579// ============================================================================
580
581#[cfg(feature = "rust_decimal")]
582mod rust_decimal_interop {
583    use super::{Decimal, DecimalError};
584    use std::str::FromStr;
585
586    impl TryFrom<rust_decimal::Decimal> for Decimal {
587        type Error = DecimalError;
588
589        /// Converts a `rust_decimal::Decimal` to a `decimal_bytes::Decimal`.
590        ///
591        /// # Example
592        ///
593        /// ```ignore
594        /// use rust_decimal::Decimal as RustDecimal;
595        /// use decimal_bytes::Decimal;
596        ///
597        /// let rd = RustDecimal::new(12345, 2); // 123.45
598        /// let d: Decimal = rd.try_into().unwrap();
599        /// assert_eq!(d.to_string(), "123.45");
600        /// ```
601        fn try_from(value: rust_decimal::Decimal) -> Result<Self, Self::Error> {
602            Decimal::from_str(&value.to_string())
603        }
604    }
605
606    impl TryFrom<&Decimal> for rust_decimal::Decimal {
607        type Error = rust_decimal::Error;
608
609        /// Converts a `decimal_bytes::Decimal` to a `rust_decimal::Decimal`.
610        ///
611        /// Note: This may fail if the decimal exceeds rust_decimal's precision limits
612        /// (28-29 significant digits) or if it's a special value (Infinity, NaN).
613        ///
614        /// # Example
615        ///
616        /// ```ignore
617        /// use rust_decimal::Decimal as RustDecimal;
618        /// use decimal_bytes::Decimal;
619        ///
620        /// let d = Decimal::from_str("123.45").unwrap();
621        /// let rd: RustDecimal = (&d).try_into().unwrap();
622        /// assert_eq!(rd.to_string(), "123.45");
623        /// ```
624        fn try_from(value: &Decimal) -> Result<Self, Self::Error> {
625            use std::str::FromStr;
626            rust_decimal::Decimal::from_str(&value.to_string())
627        }
628    }
629
630    impl TryFrom<Decimal> for rust_decimal::Decimal {
631        type Error = rust_decimal::Error;
632
633        fn try_from(value: Decimal) -> Result<Self, Self::Error> {
634            rust_decimal::Decimal::try_from(&value)
635        }
636    }
637}
638
639// The rust_decimal_interop module only contains trait implementations,
640// so there's nothing to re-export. The TryFrom impls are automatically available.
641
642// ============================================================================
643// Optional: bigdecimal interop (enabled with "bigdecimal" feature)
644// ============================================================================
645
646#[cfg(feature = "bigdecimal")]
647mod bigdecimal_interop {
648    use super::{Decimal, DecimalError};
649    use std::str::FromStr;
650
651    impl TryFrom<bigdecimal::BigDecimal> for Decimal {
652        type Error = DecimalError;
653
654        /// Converts a `bigdecimal::BigDecimal` to a `decimal_bytes::Decimal`.
655        fn try_from(value: bigdecimal::BigDecimal) -> Result<Self, Self::Error> {
656            Decimal::from_str(&value.to_string())
657        }
658    }
659
660    impl TryFrom<&bigdecimal::BigDecimal> for Decimal {
661        type Error = DecimalError;
662
663        fn try_from(value: &bigdecimal::BigDecimal) -> Result<Self, Self::Error> {
664            Decimal::from_str(&value.to_string())
665        }
666    }
667
668    impl TryFrom<&Decimal> for bigdecimal::BigDecimal {
669        type Error = bigdecimal::ParseBigDecimalError;
670
671        /// Converts a `decimal_bytes::Decimal` to a `bigdecimal::BigDecimal`.
672        ///
673        /// Note: This may fail for special values (Infinity, NaN) which
674        /// bigdecimal doesn't support.
675        fn try_from(value: &Decimal) -> Result<Self, Self::Error> {
676            use std::str::FromStr;
677            bigdecimal::BigDecimal::from_str(&value.to_string())
678        }
679    }
680
681    impl TryFrom<Decimal> for bigdecimal::BigDecimal {
682        type Error = bigdecimal::ParseBigDecimalError;
683
684        fn try_from(value: Decimal) -> Result<Self, Self::Error> {
685            bigdecimal::BigDecimal::try_from(&value)
686        }
687    }
688}
689
690// The bigdecimal_interop module only contains trait implementations,
691// so there's nothing to re-export. The TryFrom impls are automatically available.
692
693#[cfg(test)]
694mod tests {
695    use super::*;
696
697    #[test]
698    fn test_from_str() {
699        let d = Decimal::from_str("123.456").unwrap();
700        assert_eq!(d.to_string(), "123.456");
701    }
702
703    #[test]
704    fn test_zero() {
705        let d = Decimal::from_str("0").unwrap();
706        assert!(d.is_zero());
707        assert!(!d.is_negative());
708        assert!(!d.is_positive());
709        assert!(d.is_finite());
710        assert!(!d.is_special());
711    }
712
713    #[test]
714    fn test_negative() {
715        let d = Decimal::from_str("-123.456").unwrap();
716        assert!(d.is_negative());
717        assert!(!d.is_zero());
718        assert!(!d.is_positive());
719        assert!(d.is_finite());
720    }
721
722    #[test]
723    fn test_positive() {
724        let d = Decimal::from_str("123.456").unwrap();
725        assert!(d.is_positive());
726        assert!(!d.is_zero());
727        assert!(!d.is_negative());
728        assert!(d.is_finite());
729    }
730
731    #[test]
732    fn test_ordering() {
733        let values = vec!["-100", "-10", "-1", "-0.1", "0", "0.1", "1", "10", "100"];
734        let decimals: Vec<Decimal> = values
735            .iter()
736            .map(|s| Decimal::from_str(s).unwrap())
737            .collect();
738
739        // Check that ordering is correct
740        for i in 0..decimals.len() - 1 {
741            assert!(
742                decimals[i] < decimals[i + 1],
743                "{} should be < {}",
744                values[i],
745                values[i + 1]
746            );
747        }
748
749        // Check that byte ordering matches
750        for i in 0..decimals.len() - 1 {
751            assert!(
752                decimals[i].as_bytes() < decimals[i + 1].as_bytes(),
753                "bytes of {} should be < bytes of {}",
754                values[i],
755                values[i + 1]
756            );
757        }
758    }
759
760    #[test]
761    fn test_roundtrip() {
762        let values = vec![
763            "0", "1", "-1", "123.456", "-123.456", "0.001", "0.1", "10", "100", "1000000",
764            "-1000000",
765        ];
766
767        for s in values {
768            let d = Decimal::from_str(s).unwrap();
769            let bytes = d.as_bytes();
770            let restored = Decimal::from_bytes(bytes).unwrap();
771            assert_eq!(d, restored, "Roundtrip failed for {}", s);
772        }
773    }
774
775    #[test]
776    fn test_precision_scale() {
777        // Round to 2 decimal places
778        let d = Decimal::with_precision_scale("123.456", Some(10), Some(2)).unwrap();
779        assert_eq!(d.to_string(), "123.46");
780
781        // When precision is exceeded, least significant integer digits are kept
782        let d = Decimal::with_precision_scale("12345.67", Some(5), Some(2)).unwrap();
783        assert_eq!(d.to_string(), "345.67"); // 5 digits total, 2 after decimal = 3 integer digits max
784
785        // Rounding within precision limits
786        let d = Decimal::with_precision_scale("99.999", Some(5), Some(2)).unwrap();
787        assert_eq!(d.to_string(), "100"); // Rounds up, fits in precision
788    }
789
790    #[test]
791    fn test_from_integer() {
792        let d = Decimal::from(42i64);
793        assert_eq!(d.to_string(), "42");
794
795        let d = Decimal::from(-100i32);
796        assert_eq!(d.to_string(), "-100");
797    }
798
799    #[test]
800    fn test_from_f64() {
801        // Normal f64 values
802        let d: Decimal = 123.456f64.try_into().unwrap();
803        assert_eq!(d.to_string(), "123.456");
804
805        let d: Decimal = (-99.5f64).try_into().unwrap();
806        assert_eq!(d.to_string(), "-99.5");
807
808        let d: Decimal = 0.0f64.try_into().unwrap();
809        assert!(d.is_zero());
810
811        // Special values
812        let inf: Decimal = f64::INFINITY.try_into().unwrap();
813        assert!(inf.is_pos_infinity());
814
815        let neg_inf: Decimal = f64::NEG_INFINITY.try_into().unwrap();
816        assert!(neg_inf.is_neg_infinity());
817
818        let nan: Decimal = f64::NAN.try_into().unwrap();
819        assert!(nan.is_nan());
820    }
821
822    #[test]
823    fn test_serialization() {
824        let d = Decimal::from_str("123.456").unwrap();
825        let json = serde_json::to_string(&d).unwrap();
826        assert_eq!(json, "\"123.456\"");
827
828        let restored: Decimal = serde_json::from_str(&json).unwrap();
829        assert_eq!(d, restored);
830    }
831
832    #[test]
833    fn test_byte_efficiency() {
834        // Check that storage is reasonably efficient
835        let d = Decimal::from_str("123456789").unwrap();
836        // 1 byte sign + ~2 bytes exponent + ~5 bytes mantissa (9 digits / 2)
837        assert!(
838            d.byte_len() <= 10,
839            "Expected <= 10 bytes, got {}",
840            d.byte_len()
841        );
842
843        let d = Decimal::from_str("0.000001").unwrap();
844        // Should be compact for small numbers too
845        assert!(
846            d.byte_len() <= 6,
847            "Expected <= 6 bytes, got {}",
848            d.byte_len()
849        );
850    }
851
852    // ==================== Special Values Tests ====================
853
854    #[test]
855    fn test_infinity_creation() {
856        let pos_inf = Decimal::infinity();
857        assert!(pos_inf.is_pos_infinity());
858        assert!(pos_inf.is_infinity());
859        assert!(!pos_inf.is_neg_infinity());
860        assert!(!pos_inf.is_nan());
861        assert!(pos_inf.is_special());
862        assert!(!pos_inf.is_finite());
863        assert_eq!(pos_inf.to_string(), "Infinity");
864
865        let neg_inf = Decimal::neg_infinity();
866        assert!(neg_inf.is_neg_infinity());
867        assert!(neg_inf.is_infinity());
868        assert!(!neg_inf.is_pos_infinity());
869        assert!(!neg_inf.is_nan());
870        assert!(neg_inf.is_special());
871        assert!(!neg_inf.is_finite());
872        assert_eq!(neg_inf.to_string(), "-Infinity");
873    }
874
875    #[test]
876    fn test_nan_creation() {
877        let nan = Decimal::nan();
878        assert!(nan.is_nan());
879        assert!(nan.is_special());
880        assert!(!nan.is_finite());
881        assert!(!nan.is_infinity());
882        assert!(!nan.is_zero());
883        assert_eq!(nan.to_string(), "NaN");
884    }
885
886    #[test]
887    fn test_special_value_from_str() {
888        let pos_inf = Decimal::from_str("Infinity").unwrap();
889        assert!(pos_inf.is_pos_infinity());
890
891        let neg_inf = Decimal::from_str("-Infinity").unwrap();
892        assert!(neg_inf.is_neg_infinity());
893
894        let nan = Decimal::from_str("NaN").unwrap();
895        assert!(nan.is_nan());
896
897        // Case-insensitive
898        let inf = Decimal::from_str("infinity").unwrap();
899        assert!(inf.is_pos_infinity());
900
901        let inf = Decimal::from_str("INF").unwrap();
902        assert!(inf.is_pos_infinity());
903    }
904
905    #[test]
906    fn test_special_value_ordering() {
907        // PostgreSQL order: -Infinity < negatives < zero < positives < Infinity < NaN
908        let neg_inf = Decimal::neg_infinity();
909        let neg_num = Decimal::from_str("-1000").unwrap();
910        let zero = Decimal::from_str("0").unwrap();
911        let pos_num = Decimal::from_str("1000").unwrap();
912        let pos_inf = Decimal::infinity();
913        let nan = Decimal::nan();
914
915        assert!(neg_inf < neg_num);
916        assert!(neg_num < zero);
917        assert!(zero < pos_num);
918        assert!(pos_num < pos_inf);
919        assert!(pos_inf < nan);
920
921        // Verify byte ordering matches
922        assert!(neg_inf.as_bytes() < neg_num.as_bytes());
923        assert!(neg_num.as_bytes() < zero.as_bytes());
924        assert!(zero.as_bytes() < pos_num.as_bytes());
925        assert!(pos_num.as_bytes() < pos_inf.as_bytes());
926        assert!(pos_inf.as_bytes() < nan.as_bytes());
927    }
928
929    #[test]
930    fn test_special_value_equality() {
931        // All NaNs are equal (PostgreSQL semantics)
932        let nan1 = Decimal::from_str("NaN").unwrap();
933        let nan2 = Decimal::from_str("nan").unwrap();
934        let nan3 = Decimal::nan();
935        assert_eq!(nan1, nan2);
936        assert_eq!(nan2, nan3);
937
938        // Infinities are equal to themselves
939        let inf1 = Decimal::infinity();
940        let inf2 = Decimal::from_str("Infinity").unwrap();
941        assert_eq!(inf1, inf2);
942
943        let neg_inf1 = Decimal::neg_infinity();
944        let neg_inf2 = Decimal::from_str("-Infinity").unwrap();
945        assert_eq!(neg_inf1, neg_inf2);
946    }
947
948    #[test]
949    fn test_special_value_serialization() {
950        let inf = Decimal::infinity();
951        let json = serde_json::to_string(&inf).unwrap();
952        assert_eq!(json, "\"Infinity\"");
953        let restored: Decimal = serde_json::from_str(&json).unwrap();
954        assert_eq!(inf, restored);
955
956        let nan = Decimal::nan();
957        let json = serde_json::to_string(&nan).unwrap();
958        assert_eq!(json, "\"NaN\"");
959        let restored: Decimal = serde_json::from_str(&json).unwrap();
960        assert_eq!(nan, restored);
961    }
962
963    #[test]
964    fn test_special_value_byte_efficiency() {
965        // Special values should be compact (3 bytes each)
966        assert_eq!(Decimal::infinity().byte_len(), 3);
967        assert_eq!(Decimal::neg_infinity().byte_len(), 3);
968        assert_eq!(Decimal::nan().byte_len(), 3);
969    }
970
971    // ==================== Negative Scale Tests ====================
972
973    #[test]
974    fn test_negative_scale() {
975        // Round to nearest 1000
976        let d = Decimal::with_precision_scale("12345", Some(10), Some(-3)).unwrap();
977        assert_eq!(d.to_string(), "12000");
978
979        // Round up
980        let d = Decimal::with_precision_scale("12500", Some(10), Some(-3)).unwrap();
981        assert_eq!(d.to_string(), "13000");
982
983        // Round to nearest 100
984        let d = Decimal::with_precision_scale("1234", Some(10), Some(-2)).unwrap();
985        assert_eq!(d.to_string(), "1200");
986    }
987
988    #[test]
989    fn test_negative_scale_with_precision() {
990        // NUMERIC(2, -3): 2 significant digits, round to nearest 1000
991        let d = Decimal::with_precision_scale("12345", Some(2), Some(-3)).unwrap();
992        assert_eq!(d.to_string(), "12000");
993    }
994
995    // ==================== Error Handling Tests ====================
996
997    #[test]
998    fn test_invalid_format_errors() {
999        // Multiple decimal points
1000        let result = Decimal::from_str("1.2.3");
1001        assert!(result.is_err());
1002        assert!(matches!(
1003            result.unwrap_err(),
1004            DecimalError::InvalidFormat(_)
1005        ));
1006
1007        // Invalid characters
1008        let result = Decimal::from_str("12abc");
1009        assert!(result.is_err());
1010
1011        // Missing exponent after 'e'
1012        let result = Decimal::from_str("1e");
1013        assert!(result.is_err());
1014        assert!(matches!(
1015            result.unwrap_err(),
1016            DecimalError::InvalidFormat(_)
1017        ));
1018
1019        // Invalid exponent (non-numeric after 'e')
1020        let result = Decimal::from_str("1eabc");
1021        assert!(result.is_err());
1022
1023        // Empty string gives zero
1024        let d = Decimal::from_str("").unwrap();
1025        assert!(d.is_zero());
1026
1027        // Just a sign with no digits
1028        let result = Decimal::from_str("-");
1029        assert!(result.is_ok()); // Parses as zero
1030    }
1031
1032    #[test]
1033    fn test_leading_plus_sign() {
1034        let d = Decimal::from_str("+123.456").unwrap();
1035        assert_eq!(d.to_string(), "123.456");
1036        assert!(d.is_positive());
1037    }
1038
1039    #[test]
1040    fn test_scientific_notation() {
1041        let d = Decimal::from_str("1.5e10").unwrap();
1042        assert_eq!(d.to_string(), "15000000000");
1043
1044        let d = Decimal::from_str("1.5E-3").unwrap();
1045        assert_eq!(d.to_string(), "0.0015");
1046
1047        let d = Decimal::from_str("1e+5").unwrap();
1048        assert_eq!(d.to_string(), "100000");
1049    }
1050
1051    #[test]
1052    fn test_leading_decimal_point() {
1053        // ".5" should be parsed as "0.5"
1054        let d = Decimal::from_str(".5").unwrap();
1055        assert_eq!(d.to_string(), "0.5");
1056
1057        let d = Decimal::from_str("-.25").unwrap();
1058        assert_eq!(d.to_string(), "-0.25");
1059    }
1060
1061    #[test]
1062    fn test_trailing_zeros() {
1063        let d = Decimal::from_str("100").unwrap();
1064        assert_eq!(d.to_string(), "100");
1065
1066        let d = Decimal::from_str("1.500").unwrap();
1067        assert_eq!(d.to_string(), "1.5");
1068    }
1069
1070    #[test]
1071    fn test_leading_zeros() {
1072        let d = Decimal::from_str("007").unwrap();
1073        assert_eq!(d.to_string(), "7");
1074
1075        let d = Decimal::from_str("00.123").unwrap();
1076        assert_eq!(d.to_string(), "0.123");
1077    }
1078
1079    // ==================== Additional Trait Tests ====================
1080
1081    #[test]
1082    fn test_into_bytes() {
1083        let d = Decimal::from_str("123.456").unwrap();
1084        let bytes_ref = d.as_bytes().to_vec();
1085        let bytes_owned = d.into_bytes();
1086        assert_eq!(bytes_ref, bytes_owned);
1087    }
1088
1089    #[test]
1090    fn test_clone() {
1091        let d1 = Decimal::from_str("123.456").unwrap();
1092        let d2 = d1.clone();
1093        assert_eq!(d1, d2);
1094        assert_eq!(d1.as_bytes(), d2.as_bytes());
1095    }
1096
1097    #[test]
1098    fn test_hash() {
1099        use std::collections::HashSet;
1100
1101        let mut set = HashSet::new();
1102        set.insert(Decimal::from_str("123.456").unwrap());
1103        set.insert(Decimal::from_str("123.456").unwrap()); // Duplicate
1104        set.insert(Decimal::from_str("789.012").unwrap());
1105
1106        assert_eq!(set.len(), 2);
1107        assert!(set.contains(&Decimal::from_str("123.456").unwrap()));
1108    }
1109
1110    #[test]
1111    fn test_debug_format() {
1112        let d = Decimal::from_str("123.456").unwrap();
1113        let debug_str = format!("{:?}", d);
1114        assert!(debug_str.contains("Decimal"));
1115        assert!(debug_str.contains("123.456"));
1116    }
1117
1118    #[test]
1119    fn test_ord_trait() {
1120        use std::cmp::Ordering;
1121
1122        let a = Decimal::from_str("1").unwrap();
1123        let b = Decimal::from_str("2").unwrap();
1124        let c = Decimal::from_str("1").unwrap();
1125
1126        assert_eq!(a.cmp(&b), Ordering::Less);
1127        assert_eq!(b.cmp(&a), Ordering::Greater);
1128        assert_eq!(a.cmp(&c), Ordering::Equal);
1129    }
1130
1131    #[test]
1132    fn test_from_bytes_invalid() {
1133        // Empty bytes should fail
1134        let result = Decimal::from_bytes(&[]);
1135        assert!(result.is_err());
1136
1137        // Single invalid sign byte
1138        let result = Decimal::from_bytes(&[0x00]);
1139        assert!(result.is_err());
1140    }
1141
1142    #[test]
1143    fn test_deserialize_from_string_number() {
1144        // JSON string numbers deserialize correctly
1145        let d: Decimal = serde_json::from_str("\"42\"").unwrap();
1146        assert_eq!(d.to_string(), "42");
1147
1148        let d: Decimal = serde_json::from_str("\"-100\"").unwrap();
1149        assert_eq!(d.to_string(), "-100");
1150
1151        let d: Decimal = serde_json::from_str("\"1.5e10\"").unwrap();
1152        assert_eq!(d.to_string(), "15000000000");
1153    }
1154
1155    #[test]
1156    fn test_from_various_integer_types() {
1157        assert_eq!(Decimal::from(0i32).to_string(), "0");
1158        assert_eq!(Decimal::from(i32::MAX).to_string(), "2147483647");
1159        assert_eq!(Decimal::from(i32::MIN).to_string(), "-2147483648");
1160        assert_eq!(Decimal::from(i64::MAX).to_string(), "9223372036854775807");
1161        assert_eq!(Decimal::from(i64::MIN).to_string(), "-9223372036854775808");
1162    }
1163
1164    #[test]
1165    fn test_precision_overflow() {
1166        // Exponent too large
1167        let result = Decimal::from_str("1e20000");
1168        assert!(result.is_err());
1169        assert!(matches!(
1170            result.unwrap_err(),
1171            DecimalError::PrecisionOverflow
1172        ));
1173
1174        // Exponent too small (negative)
1175        let result = Decimal::from_str("1e-20000");
1176        assert!(result.is_err());
1177        assert!(matches!(
1178            result.unwrap_err(),
1179            DecimalError::PrecisionOverflow
1180        ));
1181    }
1182
1183    #[test]
1184    fn test_all_zeros_variations() {
1185        let d = Decimal::from_str("0").unwrap();
1186        assert!(d.is_zero());
1187
1188        let d = Decimal::from_str("0.0").unwrap();
1189        assert!(d.is_zero());
1190
1191        let d = Decimal::from_str("00.00").unwrap();
1192        assert!(d.is_zero());
1193
1194        let d = Decimal::from_str("-0").unwrap();
1195        assert!(d.is_zero());
1196        // Note: -0 normalizes to 0
1197    }
1198
1199    // ==================== Additional Edge Case Tests ====================
1200
1201    #[test]
1202    fn test_rounding_all_nines() {
1203        // Rounding 99.999 with scale 2 should become 100.00 -> 100
1204        let d = Decimal::with_precision_scale("99.999", Some(10), Some(2)).unwrap();
1205        assert_eq!(d.to_string(), "100");
1206
1207        // Rounding 9.99 with scale 1 should become 10.0 -> 10
1208        let d = Decimal::with_precision_scale("9.99", Some(10), Some(1)).unwrap();
1209        assert_eq!(d.to_string(), "10");
1210
1211        // 999 rounding to nearest 10 (scale -1) should become 1000
1212        let d = Decimal::with_precision_scale("999", Some(10), Some(-1)).unwrap();
1213        assert_eq!(d.to_string(), "1000");
1214    }
1215
1216    #[test]
1217    fn test_negative_scale_small_number() {
1218        // Number smaller than rounding unit, rounds to 0
1219        let d = Decimal::with_precision_scale("4", Some(10), Some(-1)).unwrap();
1220        assert_eq!(d.to_string(), "0");
1221
1222        // Number >= half the rounding unit, rounds up
1223        let d = Decimal::with_precision_scale("5", Some(10), Some(-1)).unwrap();
1224        assert_eq!(d.to_string(), "10");
1225
1226        // Negative number smaller than rounding unit
1227        let d = Decimal::with_precision_scale("-4", Some(10), Some(-1)).unwrap();
1228        assert_eq!(d.to_string(), "0");
1229
1230        // Negative number >= half unit
1231        let d = Decimal::with_precision_scale("-5", Some(10), Some(-1)).unwrap();
1232        assert_eq!(d.to_string(), "-10");
1233    }
1234
1235    #[test]
1236    fn test_precision_truncation() {
1237        // When precision is exceeded, truncate from left
1238        let d = Decimal::with_precision_scale("123456", Some(3), Some(0)).unwrap();
1239        assert_eq!(d.to_string(), "456");
1240
1241        // With decimal places
1242        let d = Decimal::with_precision_scale("12345.67", Some(4), Some(2)).unwrap();
1243        assert_eq!(d.to_string(), "45.67");
1244    }
1245
1246    #[test]
1247    fn test_very_small_numbers() {
1248        let d = Decimal::from_str("0.000000001").unwrap();
1249        assert_eq!(d.to_string(), "0.000000001");
1250        assert!(d.is_positive());
1251
1252        let d = Decimal::from_str("-0.000000001").unwrap();
1253        assert_eq!(d.to_string(), "-0.000000001");
1254        assert!(d.is_negative());
1255    }
1256
1257    #[test]
1258    fn test_very_large_numbers() {
1259        let d = Decimal::from_str("999999999999999999999999999999").unwrap();
1260        assert_eq!(d.to_string(), "999999999999999999999999999999");
1261
1262        let d = Decimal::from_str("-999999999999999999999999999999").unwrap();
1263        assert_eq!(d.to_string(), "-999999999999999999999999999999");
1264    }
1265
1266    #[test]
1267    fn test_max_exponent_boundary() {
1268        // Just under max exponent should work
1269        let d = Decimal::from_str("1e16000").unwrap();
1270        assert!(d.is_positive());
1271
1272        // Just over should fail
1273        let result = Decimal::from_str("1e17000");
1274        assert!(result.is_err());
1275    }
1276
1277    #[test]
1278    fn test_min_exponent_boundary() {
1279        // Just above min exponent should work
1280        let d = Decimal::from_str("1e-16000").unwrap();
1281        assert!(d.is_positive());
1282
1283        // Just below should fail
1284        let result = Decimal::from_str("1e-17000");
1285        assert!(result.is_err());
1286    }
1287
1288    #[test]
1289    fn test_odd_digit_count() {
1290        // Odd number of digits (tests BCD padding)
1291        let d = Decimal::from_str("12345").unwrap();
1292        assert_eq!(d.to_string(), "12345");
1293
1294        let d = Decimal::from_str("1").unwrap();
1295        assert_eq!(d.to_string(), "1");
1296
1297        let d = Decimal::from_str("123").unwrap();
1298        assert_eq!(d.to_string(), "123");
1299    }
1300
1301    #[test]
1302    fn test_negative_number_ordering() {
1303        // Verify negative number byte ordering is correct
1304        let a = Decimal::from_str("-100").unwrap();
1305        let b = Decimal::from_str("-10").unwrap();
1306        let c = Decimal::from_str("-1").unwrap();
1307
1308        // -100 < -10 < -1 numerically
1309        assert!(a < b);
1310        assert!(b < c);
1311
1312        // Byte ordering should match
1313        assert!(a.as_bytes() < b.as_bytes());
1314        assert!(b.as_bytes() < c.as_bytes());
1315    }
1316
1317    #[test]
1318    fn test_from_bytes_unchecked_roundtrip() {
1319        let original = Decimal::from_str("123.456").unwrap();
1320        let bytes = original.as_bytes().to_vec();
1321        let restored = Decimal::from_bytes_unchecked(bytes);
1322        assert_eq!(original, restored);
1323    }
1324
1325    #[test]
1326    fn test_special_value_checks() {
1327        let d = Decimal::from_str("123.456").unwrap();
1328        assert!(!d.is_nan());
1329        assert!(!d.is_infinity());
1330        assert!(!d.is_pos_infinity());
1331        assert!(!d.is_neg_infinity());
1332        assert!(!d.is_special());
1333        assert!(d.is_finite());
1334    }
1335
1336    #[test]
1337    fn test_equality_and_hash_consistency() {
1338        use std::collections::HashMap;
1339
1340        let d1 = Decimal::from_str("123.456").unwrap();
1341        let d2 = Decimal::from_str("123.456").unwrap();
1342        let d3 = Decimal::from_str("123.457").unwrap();
1343
1344        // Equal values should be equal
1345        assert_eq!(d1, d2);
1346        assert_ne!(d1, d3);
1347
1348        // Equal values should have same hash (can be used as map keys)
1349        let mut map = HashMap::new();
1350        map.insert(d1.clone(), "first");
1351        map.insert(d2.clone(), "second"); // Should overwrite
1352        assert_eq!(map.len(), 1);
1353        assert_eq!(map.get(&d1), Some(&"second"));
1354    }
1355
1356    #[test]
1357    fn test_scale_zero() {
1358        // Scale 0 should keep integer part only
1359        let d = Decimal::with_precision_scale("123.999", Some(10), Some(0)).unwrap();
1360        assert_eq!(d.to_string(), "124"); // Rounds up
1361    }
1362
1363    #[test]
1364    fn test_only_fractional_with_precision_scale() {
1365        let d = Decimal::with_precision_scale(".5", Some(10), Some(2)).unwrap();
1366        assert_eq!(d.to_string(), "0.5");
1367    }
1368
1369    #[test]
1370    fn test_default_impl() {
1371        let d = Decimal::default();
1372        assert!(d.is_zero());
1373        assert_eq!(d.to_string(), "0");
1374    }
1375
1376    #[test]
1377    fn test_precision_zero_integer_digits() {
1378        // When precision equals scale, no integer digits are allowed
1379        // This triggers the max_integer_digits == 0 branch
1380        let d = Decimal::with_precision_scale("123.456", Some(2), Some(2)).unwrap();
1381        assert_eq!(d.to_string(), "0.46");
1382    }
1383
1384    #[test]
1385    fn test_negative_with_precision_truncation() {
1386        // Negative number that gets truncated by precision constraints
1387        let d = Decimal::with_precision_scale("-123.456", Some(3), Some(2)).unwrap();
1388        assert_eq!(d.to_string(), "-3.46");
1389    }
1390
1391    #[test]
1392    fn test_invalid_sign_byte() {
1393        // Sign bytes: SIGN_NEGATIVE=0x00, SIGN_ZERO=0x80, SIGN_POSITIVE=0xFF
1394        // Any other sign byte is invalid
1395
1396        // Sign byte 0x01 is invalid
1397        let result = Decimal::from_bytes(&[0x01, 0x40, 0x00, 0x12]);
1398        assert!(result.is_err());
1399
1400        // Sign byte 0x7F is also invalid
1401        let result = Decimal::from_bytes(&[0x7F, 0x40, 0x00, 0x12]);
1402        assert!(result.is_err());
1403
1404        // Sign byte 0xFE is also invalid
1405        let result = Decimal::from_bytes(&[0xFE, 0x40, 0x00, 0x12]);
1406        assert!(result.is_err());
1407    }
1408
1409    #[test]
1410    fn test_invalid_bcd_encoding() {
1411        // Construct bytes that would decode to invalid BCD (digit > 9)
1412        // SIGN_POSITIVE = 0xFF, with valid exponent, but invalid BCD mantissa
1413        // A valid BCD digit must have each nibble 0-9. 0xAB has A=10, B=11 (both > 9)
1414        let invalid_bytes = vec![
1415            0xFF, // SIGN_POSITIVE
1416            0x80, 0x00, // Valid exponent (middle of range)
1417            0xAB, // Invalid BCD: high nibble = 10, low nibble = 11
1418        ];
1419        let result = Decimal::from_bytes(&invalid_bytes);
1420        assert!(result.is_err());
1421
1422        // Also test with just high nibble invalid
1423        let invalid_bytes = vec![
1424            0xFF, // SIGN_POSITIVE
1425            0x80, 0x00, // Valid exponent
1426            0xA1, // Invalid BCD: high nibble = 10, low nibble = 1
1427        ];
1428        let result = Decimal::from_bytes(&invalid_bytes);
1429        assert!(result.is_err());
1430
1431        // Also test with just low nibble invalid
1432        let invalid_bytes = vec![
1433            0xFF, // SIGN_POSITIVE
1434            0x80, 0x00, // Valid exponent
1435            0x1B, // Invalid BCD: high nibble = 1, low nibble = 11
1436        ];
1437        let result = Decimal::from_bytes(&invalid_bytes);
1438        assert!(result.is_err());
1439    }
1440
1441    #[test]
1442    fn test_reserved_exponent_positive() {
1443        // SIGN_POSITIVE = 0xFF
1444        // Reserved exponents: 0xFFFE (Infinity), 0xFFFF (NaN)
1445        // If we have more than 3 bytes, special value check is skipped
1446        // but decode_exponent will catch the reserved value
1447
1448        // Reserved NaN exponent (0xFFFF) with extra mantissa byte
1449        let bytes_with_reserved_exp = vec![
1450            0xFF, // SIGN_POSITIVE
1451            0xFF, 0xFF, // Reserved for NaN
1452            0x12, // Some mantissa (makes it 4 bytes, not 3)
1453        ];
1454        let result = Decimal::from_bytes(&bytes_with_reserved_exp);
1455        assert!(result.is_err());
1456
1457        // Reserved Infinity exponent (0xFFFE) with extra mantissa byte
1458        let bytes_with_reserved_exp = vec![
1459            0xFF, // SIGN_POSITIVE
1460            0xFF, 0xFE, // Reserved for Infinity
1461            0x12, // Some mantissa (makes it 4 bytes, not 3)
1462        ];
1463        let result = Decimal::from_bytes(&bytes_with_reserved_exp);
1464        assert!(result.is_err());
1465    }
1466
1467    #[test]
1468    fn test_reserved_exponent_negative() {
1469        // SIGN_NEGATIVE = 0x00
1470        // Reserved exponent for -Infinity: 0x0000
1471        // If we have more than 3 bytes, special value check is skipped
1472
1473        let bytes_with_reserved_exp = vec![
1474            0x00, // SIGN_NEGATIVE
1475            0x00, 0x00, // Reserved for -Infinity
1476            0x12, // Some mantissa (makes it 4 bytes, not 3)
1477        ];
1478        let result = Decimal::from_bytes(&bytes_with_reserved_exp);
1479        assert!(result.is_err());
1480    }
1481
1482    #[test]
1483    fn test_empty_mantissa_bytes() {
1484        // Construct bytes with valid sign and exponent but no mantissa
1485        // This should decode to 0 via the empty digits path in format_decimal
1486        let bytes_no_mantissa = vec![
1487            0xFF, // SIGN_POSITIVE
1488            0x80, 0x00, // Valid exponent
1489                  // No mantissa bytes
1490        ];
1491        let d = Decimal::from_bytes(&bytes_no_mantissa).unwrap();
1492        assert_eq!(d.to_string(), "0");
1493    }
1494
1495    // ==================== rust_decimal Interop Tests ====================
1496
1497    #[cfg(feature = "rust_decimal")]
1498    mod rust_decimal_tests {
1499        use super::*;
1500
1501        #[test]
1502        fn test_from_rust_decimal() {
1503            use rust_decimal::Decimal as RustDecimal;
1504
1505            let rd = RustDecimal::new(12345, 2); // 123.45
1506            let d: Decimal = rd.try_into().unwrap();
1507            assert_eq!(d.to_string(), "123.45");
1508        }
1509
1510        #[test]
1511        fn test_to_rust_decimal() {
1512            use rust_decimal::Decimal as RustDecimal;
1513
1514            let d = Decimal::from_str("123.45").unwrap();
1515            let rd: RustDecimal = (&d).try_into().unwrap();
1516            assert_eq!(rd.to_string(), "123.45");
1517        }
1518
1519        #[test]
1520        fn test_rust_decimal_roundtrip() {
1521            use rust_decimal::Decimal as RustDecimal;
1522
1523            let values = vec!["0", "1", "-1", "123.456", "-999.999", "0.001"];
1524
1525            for s in values {
1526                let d = Decimal::from_str(s).unwrap();
1527                let rd: RustDecimal = (&d).try_into().unwrap();
1528                let d2: Decimal = rd.try_into().unwrap();
1529                assert_eq!(d, d2, "Roundtrip failed for {}", s);
1530            }
1531        }
1532
1533        #[test]
1534        fn test_rust_decimal_arithmetic() {
1535            use rust_decimal::Decimal as RustDecimal;
1536
1537            // Start with decimal-bytes values
1538            let a = Decimal::from_str("100.50").unwrap();
1539            let b = Decimal::from_str("25.25").unwrap();
1540
1541            // Convert to rust_decimal for arithmetic
1542            let ra: RustDecimal = (&a).try_into().unwrap();
1543            let rb: RustDecimal = (&b).try_into().unwrap();
1544            let sum = ra + rb;
1545
1546            // Convert back to decimal-bytes for storage
1547            let result: Decimal = sum.try_into().unwrap();
1548            assert_eq!(result.to_string(), "125.75");
1549        }
1550
1551        #[test]
1552        fn test_rust_decimal_from_owned() {
1553            use rust_decimal::Decimal as RustDecimal;
1554
1555            // Test TryFrom<Decimal> (owned) for RustDecimal
1556            let d = Decimal::from_str("456.789").unwrap();
1557            let rd: RustDecimal = d.try_into().unwrap();
1558            assert_eq!(rd.to_string(), "456.789");
1559        }
1560
1561        #[test]
1562        fn test_rust_decimal_special_values_fail() {
1563            use rust_decimal::Decimal as RustDecimal;
1564
1565            // Infinity cannot convert to rust_decimal
1566            let inf = Decimal::infinity();
1567            let result: Result<RustDecimal, _> = (&inf).try_into();
1568            assert!(result.is_err());
1569
1570            // NaN cannot convert to rust_decimal
1571            let nan = Decimal::nan();
1572            let result: Result<RustDecimal, _> = (&nan).try_into();
1573            assert!(result.is_err());
1574        }
1575    }
1576
1577    // ==================== bigdecimal Interop Tests ====================
1578
1579    #[cfg(feature = "bigdecimal")]
1580    mod bigdecimal_tests {
1581        use super::*;
1582
1583        #[test]
1584        fn test_from_bigdecimal() {
1585            use bigdecimal::BigDecimal;
1586            use std::str::FromStr;
1587
1588            let bd = BigDecimal::from_str("123.45").unwrap();
1589            let d: Decimal = bd.try_into().unwrap();
1590            assert_eq!(d.to_string(), "123.45");
1591        }
1592
1593        #[test]
1594        fn test_to_bigdecimal() {
1595            use bigdecimal::BigDecimal;
1596
1597            let d = Decimal::from_str("123.45").unwrap();
1598            let bd: BigDecimal = (&d).try_into().unwrap();
1599            assert_eq!(bd.to_string(), "123.45");
1600        }
1601
1602        #[test]
1603        fn test_bigdecimal_roundtrip() {
1604            use bigdecimal::BigDecimal;
1605
1606            let values = vec!["0", "1", "-1", "123.456", "-999.999", "0.001"];
1607
1608            for s in values {
1609                let d = Decimal::from_str(s).unwrap();
1610                let bd: BigDecimal = (&d).try_into().unwrap();
1611                let d2: Decimal = bd.try_into().unwrap();
1612                assert_eq!(d, d2, "Roundtrip failed for {}", s);
1613            }
1614        }
1615
1616        #[test]
1617        fn test_bigdecimal_from_owned() {
1618            use bigdecimal::BigDecimal;
1619
1620            // Test TryFrom<Decimal> (owned) for BigDecimal
1621            let d = Decimal::from_str("456.789").unwrap();
1622            let bd: BigDecimal = d.try_into().unwrap();
1623            assert_eq!(bd.to_string(), "456.789");
1624        }
1625
1626        #[test]
1627        fn test_bigdecimal_from_ref() {
1628            use bigdecimal::BigDecimal;
1629            use std::str::FromStr;
1630
1631            // Test TryFrom<&BigDecimal> for Decimal
1632            let bd = BigDecimal::from_str("789.012").unwrap();
1633            let d: Decimal = (&bd).try_into().unwrap();
1634            assert_eq!(d.to_string(), "789.012");
1635        }
1636
1637        #[test]
1638        fn test_bigdecimal_special_values_fail() {
1639            use bigdecimal::BigDecimal;
1640
1641            // Infinity cannot convert to BigDecimal
1642            let inf = Decimal::infinity();
1643            let result: Result<BigDecimal, _> = (&inf).try_into();
1644            assert!(result.is_err());
1645
1646            // NaN cannot convert to BigDecimal
1647            let nan = Decimal::nan();
1648            let result: Result<BigDecimal, _> = (&nan).try_into();
1649            assert!(result.is_err());
1650        }
1651    }
1652}