Skip to main content

mf2_runtime/
host.rs

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