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}