Skip to main content

decimal_scaled/types/
unified.rs

1//! Unified decimal type: `D<S, const SCALE: u32>` — a generic
2//! `#[repr(transparent)]` wrapper over the storage integer `S`.
3//!
4//! # Why
5//!
6//! Each concrete decimal width in the crate (`D38`, `D57`, `D76`, …)
7//! was originally its own `#[repr(transparent)]` newtype. That worked
8//! but meant every per-width macro invocation, every method shell,
9//! every cross-width helper had to be duplicated by name. The
10//! [`D<S, SCALE>`](crate::D) type collapses that: the per-width
11//! struct definitions become type aliases over a single generic
12//! type, and methods can be implemented once per storage (generic
13//! over `SCALE`) rather than once per `(width, scale)` pair.
14//!
15//! # Storage parameterisation
16//!
17//! `S` is the storage integer. For the narrow primitive tiers
18//! `S` is `i32` (D9), `i64` (D18), `i128` (D38). For the wide tiers
19//! `S` is one of the `crate::wide_int::Int{192,256,384,…,4096}`
20//! types.
21//!
22//! Methods on `D<S, SCALE>` are added per-`S` in the macros / impl
23//! blocks scattered across the crate — see `types/widths.rs`, the
24//! `macros/` directory, and `policy/`. This file only carries the
25//! struct definition and the most foundational `impl`s
26//! (`Clone` / `Copy` / `Default` derivation patterns that need
27//! tighter bounds than the derive macro provides).
28//!
29//! `Debug` is deliberately NOT a blanket impl on `D<S, SCALE>`: it is
30//! emitted per-width by `decl_decimal_display!` so the output is the
31//! canonical decimal string rather than the raw integer. A blanket
32//! `Debug` would collide with the macro-emitted impls once per-width
33//! types alias `D<…, SCALE>`.
34//!
35//! # `SCALE` parameterisation
36//!
37//! `SCALE` is the base-10 exponent: the logical value of
38//! `D::<S, SCALE>(raw)` is `raw / 10^SCALE`. Same semantics as the
39//! original per-width types.
40//!
41//! # Compatibility
42//!
43//! Existing names (`D9`, `D18`, `D38`, `D57`, …, `D1232`) become
44//! type aliases of `D<…, SCALE>`. Source-compatible. The
45//! `#[repr(transparent)]` layout is preserved per storage, so the
46//! raw-bytes representation of `D38<5>` is unchanged.
47
48/// Generic scaled fixed-point decimal: storage integer `S`, base-10
49/// scale `SCALE`. The logical value is `self.0 / 10^SCALE`.
50///
51/// See the module docs for the parameterisation contract.
52#[repr(transparent)]
53pub struct D<S, const SCALE: u32>(pub S);
54
55// `Clone` / `Copy` need explicit bounds — `#[derive]` would require
56// `S: Clone + Copy` to be inferable on the struct, which isn't always
57// what we want. Hand-rolling keeps the bounds tight per-call.
58
59impl<S: Clone, const SCALE: u32> Clone for D<S, SCALE> {
60    #[inline]
61    fn clone(&self) -> Self {
62        Self(self.0.clone())
63    }
64}
65
66impl<S: Copy, const SCALE: u32> Copy for D<S, SCALE> {}
67
68// `Debug` is intentionally NOT provided here as a blanket impl. Each
69// concrete storage's `Debug` impl is emitted by the per-width display
70// macro (`decl_decimal_display!`) so the output is the canonical
71// decimal string rather than the raw integer. A blanket impl on
72// `D<S, SCALE>` would collide with those macro-emitted impls once
73// the per-width types are aliases of `D<…, SCALE>`.
74
75impl<S: PartialEq, const SCALE: u32> PartialEq for D<S, SCALE> {
76    #[inline]
77    fn eq(&self, other: &Self) -> bool {
78        self.0 == other.0
79    }
80}
81
82impl<S: Eq, const SCALE: u32> Eq for D<S, SCALE> {}
83
84impl<S: PartialOrd, const SCALE: u32> PartialOrd for D<S, SCALE> {
85    #[inline]
86    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
87        self.0.partial_cmp(&other.0)
88    }
89}
90
91impl<S: Ord, const SCALE: u32> Ord for D<S, SCALE> {
92    #[inline]
93    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
94        self.0.cmp(&other.0)
95    }
96}
97
98impl<S: core::hash::Hash, const SCALE: u32> core::hash::Hash for D<S, SCALE> {
99    #[inline]
100    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
101        self.0.hash(state);
102    }
103}