1#![warn(missing_docs)]
30#![cfg_attr(docsrs, feature(doc_cfg))]
32#![no_std]
33#![forbid(unsafe_code)]
34
35extern crate alloc;
36
37#[cfg(feature = "time-zones")]
38mod dates;
39#[cfg(feature = "intl")]
40mod numbers;
41
42use alloc::string::String;
43
44use js_sys::{JsString, Number};
45use mf2_runtime::Host;
46
47#[cfg(feature = "intl")]
48pub use numbers::{IntlNumbers, NUMBERS_HOST};
49
50#[derive(Clone, Copy, Default, Debug)]
52pub struct WebHost;
53
54pub static HOST: WebHost = WebHost;
56
57#[cfg(feature = "time-zones")]
59#[derive(Clone, Copy, Default, Debug)]
60pub struct ZonesWebHost;
61
62#[cfg(feature = "time-zones")]
64pub static ZONES_HOST: ZonesWebHost = ZonesWebHost;
65
66#[cfg(feature = "datetime-intl")]
69#[derive(Clone, Copy, Default, Debug)]
70pub struct IntlWebHost;
71
72#[cfg(feature = "datetime-intl")]
74pub static INTL_HOST: IntlWebHost = IntlWebHost;
75
76impl Host for WebHost {
77 fn nfc<'a>(&self, s: &'a str, buf: &'a mut String) -> &'a str {
78 let normalized = JsString::from(s).normalize("NFC");
79 buf.clear();
80 match normalized.as_string() {
81 Some(text) => {
82 *buf = text;
83 buf
84 }
85 None => s,
86 }
87 }
88
89 fn f64_to_text<'b>(&self, x: f64, buf: &'b mut [u8; 32]) -> Option<&'b str> {
90 if !x.is_finite() {
91 return None;
92 }
93 let text = Number::from(x).to_string_with_radix(10).ok()?.as_string()?;
94 let out = buf.get_mut(..text.len())?;
95 out.copy_from_slice(text.as_bytes());
96 core::str::from_utf8(out).ok()
97 }
98}
99
100#[cfg(feature = "time-zones")]
101impl Host for ZonesWebHost {
102 fn nfc<'a>(&self, s: &'a str, buf: &'a mut String) -> &'a str {
103 WebHost.nfc(s, buf)
104 }
105
106 fn f64_to_text<'b>(&self, x: f64, buf: &'b mut [u8; 32]) -> Option<&'b str> {
107 WebHost.f64_to_text(x, buf)
108 }
109
110 fn zone_offset(&self, zone: &str, epoch_ms: i64) -> Option<i32> {
111 dates::zone_offset(zone, epoch_ms)
112 }
113}
114
115#[cfg(feature = "datetime-intl")]
116impl Host for IntlWebHost {
117 fn nfc<'a>(&self, s: &'a str, buf: &'a mut String) -> &'a str {
118 WebHost.nfc(s, buf)
119 }
120
121 fn f64_to_text<'b>(&self, x: f64, buf: &'b mut [u8; 32]) -> Option<&'b str> {
122 WebHost.f64_to_text(x, buf)
123 }
124
125 fn zone_offset(&self, zone: &str, epoch_ms: i64) -> Option<i32> {
126 dates::zone_offset(zone, epoch_ms)
127 }
128
129 fn format_date_time(
130 &self,
131 locale: &str,
132 request: &mf2_runtime::DateTimeRequest<'_>,
133 out: &mut dyn mf2_runtime::Sink,
134 ) -> bool {
135 dates::format(locale, request, out)
136 }
137}