Skip to main content

fermat_core/
display.rs

1//! `Display` and `Debug` formatting for `Decimal`.
2//!
3//! `Display` renders the value as a human-readable decimal string, padding
4//! fractional zeros where necessary:
5//!
6//! ```text
7//! Decimal { mantissa: 1_500_000, scale: 6 }  →  "1.500000"
8//! Decimal { mantissa: -42, scale: 0 }        →  "-42"
9//! Decimal { mantissa: 5, scale: 2 }          →  "0.05"
10//! ```
11//!
12//! `Debug` renders the internal representation for diagnostic use:
13//! ```text
14//! Decimal { mantissa: 1500000, scale: 6 }
15//! ```
16
17use crate::arithmetic::POW10;
18use crate::decimal::Decimal;
19use core::fmt;
20
21impl fmt::Display for Decimal {
22    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23        if self.scale == 0 {
24            return write!(f, "{}", self.mantissa);
25        }
26
27        let abs_mantissa = self.mantissa.unsigned_abs(); // u128
28        let scale = self.scale as usize;
29        let factor = POW10[scale] as u128;
30
31        let int_part = abs_mantissa / factor;
32        let frac_part = abs_mantissa % factor;
33
34        if self.mantissa < 0 {
35            f.write_str("-")?;
36        }
37
38        write!(f, "{}", int_part)?;
39        write!(f, ".")?;
40        // Pad fractional part with leading zeros to reach `scale` digits
41        write!(f, "{:0>width$}", frac_part, width = scale)?;
42
43        Ok(())
44    }
45}
46
47impl fmt::Debug for Decimal {
48    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49        write!(
50            f,
51            "Decimal {{ mantissa: {}, scale: {} }}",
52            self.mantissa, self.scale
53        )
54    }
55}
56
57// ─── Tests ───────────────────────────────────────────────────────────────────
58
59#[cfg(test)]
60mod tests {
61    use crate::decimal::Decimal;
62
63    fn d(m: i128, s: u8) -> Decimal {
64        Decimal::new(m, s).unwrap()
65    }
66
67    fn fmt(d: Decimal) -> alloc::string::String {
68        alloc::format!("{}", d)
69    }
70
71    extern crate alloc;
72
73    #[test]
74    fn display_integer() {
75        assert_eq!(fmt(d(42, 0)), "42");
76    }
77
78    #[test]
79    fn display_negative_integer() {
80        assert_eq!(fmt(d(-42, 0)), "-42");
81    }
82
83    #[test]
84    fn display_simple_decimal() {
85        assert_eq!(fmt(d(123, 2)), "1.23");
86    }
87
88    #[test]
89    fn display_leading_zeros_in_frac() {
90        // 0.05 → mantissa=5, scale=2
91        assert_eq!(fmt(d(5, 2)), "0.05");
92    }
93
94    #[test]
95    fn display_negative_decimal() {
96        assert_eq!(fmt(d(-1_500_000, 6)), "-1.500000");
97    }
98
99    #[test]
100    fn display_usdc_amount() {
101        // 1.500000 USDC
102        assert_eq!(fmt(d(1_500_000, 6)), "1.500000");
103    }
104
105    #[test]
106    fn display_zero() {
107        assert_eq!(fmt(Decimal::ZERO), "0");
108    }
109
110    #[test]
111    fn debug_format() {
112        let s = alloc::format!("{:?}", d(42, 2));
113        assert!(s.contains("mantissa: 42"));
114        assert!(s.contains("scale: 2"));
115    }
116}