Skip to main content

mf2_runtime/
host.rs

1//! What the runtime asks of its platform:
2//! the shortest text of a float (no float-printing code in the
3//! wasm), for dates the UTC offset of a named time zone and —
4//! with the `intl` date formatter — a date formatter, and — `intl` — a number formatter
5//! with plural rules. `mf2-host-std` implements it natively and for
6//! `wasm32-wasip1`, `mf2-host-web` in the browser.
7//!
8//! Canonical equivalence is not among them: the runtime answers it itself,
9//! from the map its catalog carries (`crate::nfc_equivalent`, `plan/01`
10//! §4.3), so no host needs normalization tables.
11
12use crate::datetime::DateTimeRequest;
13use crate::number::{NumberOut, NumberRequest};
14use crate::plural::Category;
15use crate::sink::Sink;
16
17/// The platform services the runtime needs.
18pub trait Host: Sync {
19    /// The shortest decimal text that round-trips the finite `x`, written
20    /// into `buf`: any form `number-literal` accepts, with an optional `+` in
21    /// the exponent (`ryu`'s `4.2`, `1e21`, `1.5e-7` and JavaScript's
22    /// `String(x)` both qualify). `None` if the host cannot produce it.
23    fn f64_to_text<'b>(&self, x: f64, buf: &'b mut [u8; 32]) -> Option<&'b str>;
24
25    /// The UTC offset, in seconds east, of the IANA time zone `zone` at the
26    /// instant `epoch_ms` (milliseconds since the epoch). `None`: the host
27    /// has no zone data, or knows no such zone (the default) — a date/time
28    /// function that must convert an instant to a named zone then reports
29    /// *Bad Option* and a fallback value (datetime.md allows it; a wall time
30    /// in the wrong zone would be worse).
31    fn zone_offset(&self, zone: &str, epoch_ms: i64) -> Option<i32> {
32        let _ = (zone, epoch_ms);
33        None
34    }
35
36    /// The `intl` date formatter: writes `request` formatted by the host's date
37    /// formatter (the browser's `Intl.DateTimeFormat`) for `locale`, and
38    /// returns `true`; `false` (the default) when the host has none.
39    fn format_date_time(
40        &self,
41        locale: &str,
42        request: &DateTimeRequest<'_>,
43        out: &mut dyn Sink,
44    ) -> bool {
45        let _ = (locale, request, out);
46        false
47    }
48
49    /// `intl`: the host's number formatter (in the browser `Intl.NumberFormat`
50    /// and `Intl.PluralRules`, when the engine has `Intl.NumberFormat` v3);
51    /// `None` (the default) when it has none — the numeric functions of an
52    /// `intl` client then show exact digits and report *Unsupported
53    /// Operation*.
54    fn numbers(&self) -> Option<&dyn NumberFormatter> {
55        None
56    }
57}
58
59/// A number formatter for the `intl` option ([`Host::numbers`]):
60/// the final "value + resolved options →
61/// text" step and the plural category, where the numeric functions keep
62/// MF2's semantics in Rust. `mf2-host-web` implements it with `Intl`.
63pub trait NumberFormatter: Sync {
64    /// Writes `request` formatted for `locale` — or, when `request.neutral`,
65    /// in neutral symbols — as text or sub-parts, and returns `true`;
66    /// `false` when it cannot (nothing written).
67    fn format(&self, locale: &str, request: &NumberRequest<'_>, out: NumberOut<'_>) -> bool;
68
69    /// The plural category of `request.value` under its digit options and
70    /// plural type (`request.ordinal`) for `locale`; `None` when it cannot.
71    fn plural(&self, locale: &str, request: &NumberRequest<'_>) -> Option<Category>;
72}