calternal_money::units
Exact money units: the ISO 4217 scale table and decimal text <-> minor units.
DESIGN §48 and #404 decide the rules this module enforces:
- Amounts in files are plain ASCII decimals,
[+-]?[0-9]+(\.[0-9]+)?, with no digit grouping. Decimal text is never parsed as a floating-point value. - The scale of a currency comes from the checked-in SIX ISO 4217 List One snapshot (published 2026-09-17). An unknown code, or a code without a numeric minor unit, is an error. The scale is never guessed.
- Scaling never rounds. Extra fraction digits are accepted only when they
are zeros (
1.230 USDis 123 minor units;1.231 USDis an error).
One calternal-specific bound is added (#462): every amount and every derived
total must stay within MAX_EXACT (2^53 - 1) minor units. The API sends
amounts as JSON integers and the web client reads them as JavaScript
numbers, which are exact only up to that bound. The Python oracle uses
unbounded integers, so a larger amount is a deliberate, documented
divergence: the codec still round-trips its text byte for byte, and only
arithmetic rejects it.
Source: crates/calternal-money/src/units.rs
Structs
Section titled “Structs”pub struct Minor(i64);An exact signed amount in the minor unit of one currency.
Invariant: |value| <= MAX_EXACT. Every constructor and every arithmetic
method checks the bound, so a Minor in hand is always exact in JSON.
Implements: Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, fmt::Display
Minor::ZERO
Section titled “Minor::ZERO”pub const ZERO: MinorZero minor units.
Minor::new
Section titled “Minor::new”pub fn new(value: i64) -> Result<Self>Wrap a raw value; fails when it is outside ±MAX_EXACT.
Minor::get
Section titled “Minor::get”pub const fn get(self) -> i64The raw signed minor units.
Minor::checked_add
Section titled “Minor::checked_add”pub fn checked_add(self, other: Minor) -> Result<Minor>Exact sum; fails instead of wrapping or leaving the exact range.
Minor::checked_sub
Section titled “Minor::checked_sub”pub fn checked_sub(self, other: Minor) -> Result<Minor>Exact difference; fails instead of wrapping or leaving the exact range.
Minor::neg
Section titled “Minor::neg”pub const fn neg(self) -> MinorExact negation. Always succeeds because the range is symmetric.
Minor::add_assign
Section titled “Minor::add_assign”pub fn add_assign(&mut self, other: Minor) -> Result<()>Add other in place (see Minor::checked_add).
Minor::sub_assign
Section titled “Minor::sub_assign”pub fn sub_assign(&mut self, other: Minor) -> Result<()>Subtract other in place (see Minor::checked_sub).
Minor::positive_part
Section titled “Minor::positive_part”pub fn positive_part(self) -> MinorThe larger of self and zero.
Minor::is_negative
Section titled “Minor::is_negative”pub const fn is_negative(self) -> boolTrue when the amount is below zero.
Minor::is_positive
Section titled “Minor::is_positive”pub const fn is_positive(self) -> boolTrue when the amount is above zero.
Source: crates/calternal-money/src/units.rs:84
Functions
Section titled “Functions”all_scales
Section titled “all_scales”pub fn all_scales() -> impl Iterator<Item = (&'static str, u32)>Every supported code with its exponent, in table order (for tests and docs).
Source: crates/calternal-money/src/units.rs:70
format_minor
Section titled “format_minor”pub fn format_minor(amount: Minor, currency: &str) -> Result<String>Format minor units as plain decimal text with an explicit sign.
Mirrors decimal_amount in the oracle: +12.34, -0.05, 0.00, no digit
grouping, exactly scale(currency) fraction digits. Round-trip invariant:
parse_minor(&format_minor(x, c)?, c)? == x for every valid x and c.
Source: crates/calternal-money/src/units.rs:214
format_unsigned_positive
Section titled “format_unsigned_positive”pub fn format_unsigned_positive(amount: Minor, currency: &str) -> Result<String>Format without a leading + (for assignment rows and targets, whose spike
examples are unsigned, such as 650.00 USD). Negative values keep -.
Source: crates/calternal-money/src/units.rs:236
is_plain_decimal
Section titled “is_plain_decimal”pub fn is_plain_decimal(text: &str) -> boolTrue when text is a plain ASCII decimal: [+-]?[0-9]+(\.[0-9]+)?.
This is the codec’s grammar check. It does not look at a currency, so the codec keeps any precision exactly as written (#404 rule 3).
Source: crates/calternal-money/src/units.rs:157
parse_minor
Section titled “parse_minor”pub fn parse_minor(text: &str, currency: &str) -> Result<Minor>Scale decimal text to exact minor units of currency, without rounding.
Mirrors minor_units in the oracle: surrounding spaces and tabs are
ignored; -0.00 is zero; a fraction longer than the scale is accepted only
when every extra digit is 0. Errors: unsupported currency, malformed
text, excess precision, or a value outside ±MAX_EXACT.
Source: crates/calternal-money/src/units.rs:174
pub fn scale(code: &str) -> Option<u32>Return the ISO 4217 exponent of code, or None for an unsupported code.
Only exact three-letter uppercase codes from the snapshot match. A lookup is a linear scan over a short constant string; it allocates nothing.
Source: crates/calternal-money/src/units.rs:59
Constants
Section titled “Constants”MAX_EXACT
Section titled “MAX_EXACT”pub const MAX_EXACT: i64The largest magnitude, in minor units, that any amount or total may have.
Equal to JavaScript’s Number.MAX_SAFE_INTEGER, so every value the API
returns is exact in a browser.