Skip to content

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 USD is 123 minor units; 1.231 USD is 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

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

pub const ZERO: Minor

Zero minor units.

pub fn new(value: i64) -> Result<Self>

Wrap a raw value; fails when it is outside ±MAX_EXACT.

pub const fn get(self) -> i64

The raw signed minor units.

pub fn checked_add(self, other: Minor) -> Result<Minor>

Exact sum; fails instead of wrapping or leaving the exact range.

pub fn checked_sub(self, other: Minor) -> Result<Minor>

Exact difference; fails instead of wrapping or leaving the exact range.

pub const fn neg(self) -> Minor

Exact negation. Always succeeds because the range is symmetric.

pub fn add_assign(&mut self, other: Minor) -> Result<()>

Add other in place (see Minor::checked_add).

pub fn sub_assign(&mut self, other: Minor) -> Result<()>

Subtract other in place (see Minor::checked_sub).

pub fn positive_part(self) -> Minor

The larger of self and zero.

pub const fn is_negative(self) -> bool

True when the amount is below zero.

pub const fn is_positive(self) -> bool

True when the amount is above zero.

Source: crates/calternal-money/src/units.rs:84

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

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

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

pub fn is_plain_decimal(text: &str) -> bool

True 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

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

pub const MAX_EXACT: i64

The 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.

Source: crates/calternal-money/src/units.rs:27