1use super::*;
8use alloc::format;
9use alloc::rc::Rc;
10use alloc::string::{String, ToString};
11use alloc::vec;
12use alloc::vec::Vec;
13
14pub(crate) fn create_intl_object() -> Value {
16 let intl = Obj::plain();
17
18 intl.borrow_mut().props.insert(
20 "getCanonicalLocales".into(),
21 nv("Intl.getCanonicalLocales", intl_get_canonical_locales),
22 );
23
24 let number_format = create_intl_constructor(
26 "NumberFormat",
27 intl_number_format_ctor,
28 vec![
29 ("format", intl_number_format_format),
30 ("formatToParts", intl_number_format_format_to_parts),
31 ("resolvedOptions", intl_number_format_resolved_options),
32 ],
33 );
34 intl.borrow_mut().props.insert("NumberFormat".into(), number_format);
35
36 let datetime_format = create_intl_constructor(
38 "DateTimeFormat",
39 intl_datetime_format_ctor,
40 vec![
41 ("format", intl_datetime_format_format),
42 ("formatToParts", intl_datetime_format_format_to_parts),
43 ("resolvedOptions", intl_datetime_format_resolved_options),
44 ],
45 );
46 intl.borrow_mut().props.insert("DateTimeFormat".into(), datetime_format);
47
48 let collator = create_intl_constructor(
50 "Collator",
51 intl_collator_ctor,
52 vec![
53 ("compare", intl_collator_compare),
54 ("resolvedOptions", intl_collator_resolved_options),
55 ],
56 );
57 intl.borrow_mut().props.insert("Collator".into(), collator);
58
59 let plural_rules = create_intl_constructor(
61 "PluralRules",
62 intl_plural_rules_ctor,
63 vec![
64 ("select", intl_plural_rules_select),
65 ("resolvedOptions", intl_plural_rules_resolved_options),
66 ],
67 );
68 intl.borrow_mut().props.insert("PluralRules".into(), plural_rules);
69
70 let relative_time_format = create_intl_constructor(
72 "RelativeTimeFormat",
73 intl_relative_time_format_ctor,
74 vec![
75 ("format", intl_relative_time_format_format),
76 ("formatToParts", intl_relative_time_format_format_to_parts),
77 ("resolvedOptions", intl_relative_time_format_resolved_options),
78 ],
79 );
80 intl.borrow_mut().props.insert("RelativeTimeFormat".into(), relative_time_format);
81
82 let segmenter = create_intl_constructor(
84 "Segmenter",
85 intl_segmenter_ctor,
86 vec![
87 ("segment", intl_segmenter_segment),
88 ("resolvedOptions", intl_segmenter_resolved_options),
89 ],
90 );
91 intl.borrow_mut().props.insert("Segmenter".into(), segmenter);
92
93 let list_format = create_intl_constructor(
95 "ListFormat",
96 intl_list_format_ctor,
97 vec![
98 ("format", intl_list_format_format),
99 ("formatToParts", intl_list_format_format_to_parts),
100 ("resolvedOptions", intl_list_format_resolved_options),
101 ],
102 );
103 intl.borrow_mut().props.insert("ListFormat".into(), list_format);
104
105 let display_names = create_intl_constructor(
107 "DisplayNames",
108 intl_display_names_ctor,
109 vec![
110 ("of", intl_display_names_of),
111 ("resolvedOptions", intl_display_names_resolved_options),
112 ],
113 );
114 intl.borrow_mut().props.insert("DisplayNames".into(), display_names);
115
116 let duration_format = create_intl_constructor(
118 "DurationFormat",
119 intl_duration_format_ctor,
120 vec![
121 ("format", intl_duration_format_format),
122 ("formatToParts", intl_duration_format_format_to_parts),
123 ("resolvedOptions", intl_duration_format_resolved_options),
124 ],
125 );
126 intl.borrow_mut().props.insert("DurationFormat".into(), duration_format);
127
128 Value::Object(intl)
129}
130
131fn create_intl_constructor(
132 name: &str,
133 ctor_fn: NativeFn,
134 methods: Vec<(&'static str, NativeFn)>,
135) -> Value {
136 let proto = Obj::plain();
137 for (m_name, m_fn) in methods {
138 proto.borrow_mut().props.insert(m_name.into(), nv(m_name, m_fn));
139 }
140 let ctor = Obj::native(name, ctor_fn);
141 ctor.borrow_mut().props.insert("prototype".into(), Value::Object(proto));
142 Value::Object(ctor)
143}
144
145fn parse_locale(val: &Value) -> String {
146 let s = val.to_js_string();
147 if s.is_empty() || s == "undefined" {
148 String::from("en-US")
149 } else {
150 match s.as_str() {
151 "en" => String::from("en-US"),
152 "ja" => String::from("ja-JP"),
153 "zh" => String::from("zh-CN"),
154 "fr" => String::from("fr-FR"),
155 "de" => String::from("de-DE"),
156 "es" => String::from("es-ES"),
157 _ => s,
158 }
159 }
160}
161
162pub(crate) fn intl_get_canonical_locales(_it: &mut Interp, _t: Value, a: &[Value]) -> Result<Value, Value> {
164 let arg0 = arg(a, 0);
165 let mut locales = Vec::new();
166 if let Value::Object(o) = &arg0 {
167 if let ObjKind::Array(items) = &o.borrow().kind {
168 for item in items {
169 let loc = parse_locale(item);
170 if !locales.contains(&loc) {
171 locales.push(loc);
172 }
173 }
174 } else {
175 locales.push(parse_locale(&arg0));
176 }
177 } else {
178 locales.push(parse_locale(&arg0));
179 }
180 let res_items: Vec<Value> = locales.into_iter().map(|s| Value::Str(Rc::new(s))).collect();
181 Ok(Value::Object(Obj::array(res_items)))
182}
183
184fn resolve_intl_this(it: &Interp, this: Value, ctor_name: &str) -> super::super::value::ObjRef {
198 let intl_val = it.global.borrow().vars.get("Intl").cloned();
199 let proto = intl_val.and_then(|v| match v {
200 Value::Object(intl) => match intl.borrow().props.get(ctor_name) {
201 Some(Value::Object(ctor)) => match ctor.borrow().props.get("prototype") {
202 Some(Value::Object(p)) => Some(p.clone()),
203 _ => None,
204 },
205 _ => None,
206 },
207 _ => None,
208 });
209 if let Value::Object(o) = &this {
210 let is_own_instance = match (&o.borrow().proto, &proto) {
211 (Some(op), Some(p)) => Rc::ptr_eq(op, p),
212 _ => false,
213 };
214 if is_own_instance {
215 return o.clone();
216 }
217 }
218 let obj = Obj::plain();
219 if let Some(p) = proto {
220 obj.borrow_mut().proto = Some(p);
221 }
222 obj
223}
224
225pub(crate) fn intl_number_format_ctor(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
227 let locale = parse_locale(&arg(a, 0));
228 let obj = resolve_intl_this(it, this, "NumberFormat");
229 obj.borrow_mut().props.insert("_intl_type".into(), Value::Str(Rc::new("NumberFormat".into())));
230 obj.borrow_mut().props.insert("_locale".into(), Value::Str(Rc::new(locale)));
231 Ok(Value::Object(obj))
232}
233
234fn group_thousands(num: f64) -> String {
243 if !num.is_finite() {
244 return format!("{}", num);
245 }
246 let negative = num.is_sign_negative() && num != 0.0;
247 let abs = libm::fabs(num);
248 let s = format!("{}", abs);
249 let (int_part, frac_part) = match s.split_once('.') {
250 Some((i, f)) => (i, Some(f)),
251 None => (s.as_str(), None),
252 };
253 let bytes = int_part.as_bytes();
254 let mut grouped = String::new();
255 for (i, b) in bytes.iter().enumerate() {
256 if i > 0 && (bytes.len() - i) % 3 == 0 {
257 grouped.push(',');
258 }
259 grouped.push(*b as char);
260 }
261 let mut out = String::new();
262 if negative {
263 out.push('-');
264 }
265 out.push_str(&grouped);
266 if let Some(f) = frac_part {
267 out.push('.');
268 out.push_str(f);
269 }
270 out
271}
272
273pub(crate) fn intl_number_format_format(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
274 let num = arg(a, 0).to_number();
275 let locale = if let Value::Object(o) = &this {
276 o.borrow().props.get("_locale").cloned().unwrap_or(Value::Str(Rc::new("en-US".into()))).to_js_string()
277 } else {
278 String::from("en-US")
279 };
280
281 let _ = locale;
282 let formatted = group_thousands(num);
283 Ok(Value::Str(Rc::new(formatted)))
284}
285
286pub(crate) fn intl_number_format_format_to_parts(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
287 let num = arg(a, 0).to_number();
288 let num_str = format!("{}", num);
289 let parts = vec![
290 {
291 let p = Obj::plain();
292 p.borrow_mut().props.insert("type".into(), Value::Str(Rc::new("integer".into())));
293 p.borrow_mut().props.insert("value".into(), Value::Str(Rc::new(num_str)));
294 Value::Object(p)
295 }
296 ];
297 let _ = this;
298 Ok(Value::Object(Obj::array(parts)))
299}
300
301pub(crate) fn intl_number_format_resolved_options(_it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
302 let locale = if let Value::Object(o) = &this {
303 o.borrow().props.get("_locale").cloned().unwrap_or(Value::Str(Rc::new("en-US".into()))).to_js_string()
304 } else {
305 String::from("en-US")
306 };
307 let res = Obj::plain();
308 res.borrow_mut().props.insert("locale".into(), Value::Str(Rc::new(locale)));
309 res.borrow_mut().props.insert("numberingSystem".into(), Value::Str(Rc::new("latn".into())));
310 res.borrow_mut().props.insert("style".into(), Value::Str(Rc::new("decimal".into())));
311 Ok(Value::Object(res))
312}
313
314pub(crate) fn intl_datetime_format_ctor(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
316 let locale = parse_locale(&arg(a, 0));
317 let obj = match this {
318 Value::Object(o) => o,
319 _ => Obj::plain(),
320 };
321 obj.borrow_mut().props.insert("_intl_type".into(), Value::Str(Rc::new("DateTimeFormat".into())));
322 obj.borrow_mut().props.insert("_locale".into(), Value::Str(Rc::new(locale)));
323 Ok(Value::Object(obj))
324}
325
326pub(crate) fn intl_datetime_format_format(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
327 let val = arg(a, 0);
328 let _ = this;
329 let formatted = val.to_js_string();
330 Ok(Value::Str(Rc::new(formatted)))
331}
332
333pub(crate) fn intl_datetime_format_format_to_parts(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
334 let val = arg(a, 0);
335 let formatted = val.to_js_string();
336 let parts = vec![
337 {
338 let p = Obj::plain();
339 p.borrow_mut().props.insert("type".into(), Value::Str(Rc::new("literal".into())));
340 p.borrow_mut().props.insert("value".into(), Value::Str(Rc::new(formatted)));
341 Value::Object(p)
342 }
343 ];
344 let _ = this;
345 Ok(Value::Object(Obj::array(parts)))
346}
347
348pub(crate) fn intl_datetime_format_resolved_options(_it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
349 let locale = if let Value::Object(o) = &this {
350 o.borrow().props.get("_locale").cloned().unwrap_or(Value::Str(Rc::new("en-US".into()))).to_js_string()
351 } else {
352 String::from("en-US")
353 };
354 let res = Obj::plain();
355 res.borrow_mut().props.insert("locale".into(), Value::Str(Rc::new(locale)));
356 res.borrow_mut().props.insert("calendar".into(), Value::Str(Rc::new("gregory".into())));
357 res.borrow_mut().props.insert("timeZone".into(), Value::Str(Rc::new("UTC".into())));
358 Ok(Value::Object(res))
359}
360
361pub(crate) fn intl_collator_ctor(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
363 let locale = parse_locale(&arg(a, 0));
364 let obj = match this {
365 Value::Object(o) => o,
366 _ => Obj::plain(),
367 };
368 obj.borrow_mut().props.insert("_intl_type".into(), Value::Str(Rc::new("Collator".into())));
369 obj.borrow_mut().props.insert("_locale".into(), Value::Str(Rc::new(locale)));
370 Ok(Value::Object(obj))
371}
372
373pub(crate) fn intl_collator_compare(_it: &mut Interp, _this: Value, a: &[Value]) -> Result<Value, Value> {
374 let s1 = arg(a, 0).to_js_string();
375 let s2 = arg(a, 1).to_js_string();
376 let cmp = s1.cmp(&s2) as i32;
377 Ok(Value::Number(cmp as f64))
378}
379
380pub(crate) fn intl_collator_resolved_options(_it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
381 let locale = if let Value::Object(o) = &this {
382 o.borrow().props.get("_locale").cloned().unwrap_or(Value::Str(Rc::new("en-US".into()))).to_js_string()
383 } else {
384 String::from("en-US")
385 };
386 let res = Obj::plain();
387 res.borrow_mut().props.insert("locale".into(), Value::Str(Rc::new(locale)));
388 res.borrow_mut().props.insert("usage".into(), Value::Str(Rc::new("sort".into())));
389 res.borrow_mut().props.insert("sensitivity".into(), Value::Str(Rc::new("variant".into())));
390 Ok(Value::Object(res))
391}
392
393pub(crate) fn intl_plural_rules_ctor(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
395 let locale = parse_locale(&arg(a, 0));
396 let obj = match this {
397 Value::Object(o) => o,
398 _ => Obj::plain(),
399 };
400 obj.borrow_mut().props.insert("_intl_type".into(), Value::Str(Rc::new("PluralRules".into())));
401 obj.borrow_mut().props.insert("_locale".into(), Value::Str(Rc::new(locale)));
402 Ok(Value::Object(obj))
403}
404
405pub(crate) fn intl_plural_rules_select(_it: &mut Interp, _this: Value, a: &[Value]) -> Result<Value, Value> {
406 let n = arg(a, 0).to_number();
407 let category = if n == 1.0 { "one" } else { "other" };
408 Ok(Value::Str(Rc::new(category.into())))
409}
410
411pub(crate) fn intl_plural_rules_resolved_options(_it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
412 let locale = if let Value::Object(o) = &this {
413 o.borrow().props.get("_locale").cloned().unwrap_or(Value::Str(Rc::new("en-US".into()))).to_js_string()
414 } else {
415 String::from("en-US")
416 };
417 let res = Obj::plain();
418 res.borrow_mut().props.insert("locale".into(), Value::Str(Rc::new(locale)));
419 res.borrow_mut().props.insert("type".into(), Value::Str(Rc::new("cardinal".into())));
420 Ok(Value::Object(res))
421}
422
423pub(crate) fn intl_relative_time_format_ctor(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
425 let locale = parse_locale(&arg(a, 0));
426 let obj = match this {
427 Value::Object(o) => o,
428 _ => Obj::plain(),
429 };
430 obj.borrow_mut().props.insert("_intl_type".into(), Value::Str(Rc::new("RelativeTimeFormat".into())));
431 obj.borrow_mut().props.insert("_locale".into(), Value::Str(Rc::new(locale)));
432 Ok(Value::Object(obj))
433}
434
435pub(crate) fn intl_relative_time_format_format(_it: &mut Interp, _this: Value, a: &[Value]) -> Result<Value, Value> {
436 let value = arg(a, 0).to_number();
437 let unit = arg(a, 1).to_js_string();
438 let formatted = if value < 0.0 {
439 format!("{} {}s ago", -value, unit)
440 } else {
441 format!("in {} {}s", value, unit)
442 };
443 Ok(Value::Str(Rc::new(formatted)))
444}
445
446pub(crate) fn intl_relative_time_format_format_to_parts(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
447 let res = intl_relative_time_format_format(_it, this, a)?;
448 let parts = vec![
449 {
450 let p = Obj::plain();
451 p.borrow_mut().props.insert("type".into(), Value::Str(Rc::new("literal".into())));
452 p.borrow_mut().props.insert("value".into(), res);
453 Value::Object(p)
454 }
455 ];
456 Ok(Value::Object(Obj::array(parts)))
457}
458
459pub(crate) fn intl_relative_time_format_resolved_options(_it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
460 let locale = if let Value::Object(o) = &this {
461 o.borrow().props.get("_locale").cloned().unwrap_or(Value::Str(Rc::new("en-US".into()))).to_js_string()
462 } else {
463 String::from("en-US")
464 };
465 let res = Obj::plain();
466 res.borrow_mut().props.insert("locale".into(), Value::Str(Rc::new(locale)));
467 res.borrow_mut().props.insert("style".into(), Value::Str(Rc::new("long".into())));
468 res.borrow_mut().props.insert("numeric".into(), Value::Str(Rc::new("always".into())));
469 Ok(Value::Object(res))
470}
471
472pub(crate) fn intl_segmenter_ctor(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
474 let locale = parse_locale(&arg(a, 0));
475 let obj = match this {
476 Value::Object(o) => o,
477 _ => Obj::plain(),
478 };
479 obj.borrow_mut().props.insert("_intl_type".into(), Value::Str(Rc::new("Segmenter".into())));
480 obj.borrow_mut().props.insert("_locale".into(), Value::Str(Rc::new(locale)));
481 Ok(Value::Object(obj))
482}
483
484pub(crate) fn intl_segmenter_segment(_it: &mut Interp, _this: Value, a: &[Value]) -> Result<Value, Value> {
485 let str_val = arg(a, 0).to_js_string();
486 let segments: Vec<Value> = str_val
487 .chars()
488 .enumerate()
489 .map(|(idx, ch)| {
490 let seg = Obj::plain();
491 seg.borrow_mut().props.insert("segment".into(), Value::Str(Rc::new(ch.to_string())));
492 seg.borrow_mut().props.insert("index".into(), Value::Number(idx as f64));
493 seg.borrow_mut().props.insert("input".into(), Value::Str(Rc::new(str_val.clone())));
494 Value::Object(seg)
495 })
496 .collect();
497 let result = Obj::plain();
498 result.borrow_mut().props.insert("containing".into(), nv("containing", |_it, _this, _a| Ok(Value::Undefined)));
499 result.borrow_mut().props.insert("_segments".into(), Value::Object(Obj::array(segments)));
500 Ok(Value::Object(result))
501}
502
503pub(crate) fn intl_segmenter_resolved_options(_it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
504 let locale = if let Value::Object(o) = &this {
505 o.borrow().props.get("_locale").cloned().unwrap_or(Value::Str(Rc::new("en-US".into()))).to_js_string()
506 } else {
507 String::from("en-US")
508 };
509 let res = Obj::plain();
510 res.borrow_mut().props.insert("locale".into(), Value::Str(Rc::new(locale)));
511 res.borrow_mut().props.insert("granularity".into(), Value::Str(Rc::new("grapheme".into())));
512 Ok(Value::Object(res))
513}
514
515pub(crate) fn intl_list_format_ctor(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
517 let locale = parse_locale(&arg(a, 0));
518 let obj = match this {
519 Value::Object(o) => o,
520 _ => Obj::plain(),
521 };
522 obj.borrow_mut().props.insert("_intl_type".into(), Value::Str(Rc::new("ListFormat".into())));
523 obj.borrow_mut().props.insert("_locale".into(), Value::Str(Rc::new(locale)));
524 Ok(Value::Object(obj))
525}
526
527pub(crate) fn intl_list_format_format(_it: &mut Interp, _this: Value, a: &[Value]) -> Result<Value, Value> {
528 let arg0 = arg(a, 0);
529 let mut items = Vec::new();
530 if let Value::Object(o) = &arg0 {
531 if let ObjKind::Array(list) = &o.borrow().kind {
532 for it in list {
533 items.push(it.to_js_string());
534 }
535 }
536 }
537 let formatted = items.join(", ");
538 Ok(Value::Str(Rc::new(formatted)))
539}
540
541pub(crate) fn intl_list_format_format_to_parts(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
542 let res = intl_list_format_format(_it, this, a)?;
543 let parts = vec![
544 {
545 let p = Obj::plain();
546 p.borrow_mut().props.insert("type".into(), Value::Str(Rc::new("element".into())));
547 p.borrow_mut().props.insert("value".into(), res);
548 Value::Object(p)
549 }
550 ];
551 Ok(Value::Object(Obj::array(parts)))
552}
553
554pub(crate) fn intl_list_format_resolved_options(_it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
555 let locale = if let Value::Object(o) = &this {
556 o.borrow().props.get("_locale").cloned().unwrap_or(Value::Str(Rc::new("en-US".into()))).to_js_string()
557 } else {
558 String::from("en-US")
559 };
560 let res = Obj::plain();
561 res.borrow_mut().props.insert("locale".into(), Value::Str(Rc::new(locale)));
562 res.borrow_mut().props.insert("type".into(), Value::Str(Rc::new("conjunction".into())));
563 res.borrow_mut().props.insert("style".into(), Value::Str(Rc::new("long".into())));
564 Ok(Value::Object(res))
565}
566
567pub(crate) fn intl_display_names_ctor(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
569 let locale = parse_locale(&arg(a, 0));
570 let obj = match this {
571 Value::Object(o) => o,
572 _ => Obj::plain(),
573 };
574 obj.borrow_mut().props.insert("_intl_type".into(), Value::Str(Rc::new("DisplayNames".into())));
575 obj.borrow_mut().props.insert("_locale".into(), Value::Str(Rc::new(locale)));
576 Ok(Value::Object(obj))
577}
578
579pub(crate) fn intl_display_names_of(_it: &mut Interp, _this: Value, a: &[Value]) -> Result<Value, Value> {
580 let code = arg(a, 0).to_js_string();
581 Ok(Value::Str(Rc::new(code)))
582}
583
584pub(crate) fn intl_display_names_resolved_options(_it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
585 let locale = if let Value::Object(o) = &this {
586 o.borrow().props.get("_locale").cloned().unwrap_or(Value::Str(Rc::new("en-US".into()))).to_js_string()
587 } else {
588 String::from("en-US")
589 };
590 let res = Obj::plain();
591 res.borrow_mut().props.insert("locale".into(), Value::Str(Rc::new(locale)));
592 res.borrow_mut().props.insert("style".into(), Value::Str(Rc::new("long".into())));
593 res.borrow_mut().props.insert("fallback".into(), Value::Str(Rc::new("code".into())));
594 Ok(Value::Object(res))
595}
596
597pub(crate) fn intl_duration_format_ctor(_it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
599 let locale = parse_locale(&arg(a, 0));
600 let mut style = String::from("long");
601 if let Value::Object(opts) = arg(a, 1) {
602 if let Some(s) = opts.borrow().props.get("style") {
603 style = s.to_js_string();
604 }
605 }
606 let obj = match this {
607 Value::Object(o) => o,
608 _ => Obj::plain(),
609 };
610 obj.borrow_mut().props.insert("_intl_type".into(), Value::Str(Rc::new("DurationFormat".into())));
611 obj.borrow_mut().props.insert("_locale".into(), Value::Str(Rc::new(locale)));
612 obj.borrow_mut().props.insert("_style".into(), Value::Str(Rc::new(style)));
613 Ok(Value::Object(obj))
614}
615
616pub(crate) fn intl_duration_format_format(_it: &mut Interp, _this: Value, a: &[Value]) -> Result<Value, Value> {
617 let dur_obj = arg(a, 0);
618 let units = [
619 ("years", "year", "years"),
620 ("months", "month", "months"),
621 ("weeks", "week", "weeks"),
622 ("days", "day", "days"),
623 ("hours", "hour", "hours"),
624 ("minutes", "minute", "minutes"),
625 ("seconds", "second", "seconds"),
626 ("milliseconds", "millisecond", "milliseconds"),
627 ("microseconds", "microsecond", "microseconds"),
628 ("nanoseconds", "nanosecond", "nanoseconds"),
629 ];
630
631 let mut parts = Vec::new();
632 if let Value::Object(o) = &dur_obj {
633 let props = &o.borrow().props;
634 for &(key, singular, plural) in &units {
635 if let Some(val) = props.get(key) {
636 let num = val.to_number();
637 if num.is_finite() && num != 0.0 {
638 let unit_str = if num.abs() == 1.0 { singular } else { plural };
639 parts.push(format!("{} {}", num, unit_str));
640 }
641 }
642 }
643 }
644 let formatted = if parts.is_empty() {
645 String::from("0 seconds")
646 } else {
647 parts.join(", ")
648 };
649 Ok(Value::Str(Rc::new(formatted)))
650}
651
652pub(crate) fn intl_duration_format_format_to_parts(it: &mut Interp, this: Value, a: &[Value]) -> Result<Value, Value> {
653 let formatted = intl_duration_format_format(it, this, a)?;
654 let p = Obj::plain();
655 p.borrow_mut().props.insert("type".into(), Value::Str(Rc::new("integer".into())));
656 p.borrow_mut().props.insert("value".into(), formatted);
657 Ok(Value::Object(Obj::array(vec![Value::Object(p)])))
658}
659
660pub(crate) fn intl_duration_format_resolved_options(_it: &mut Interp, this: Value, _a: &[Value]) -> Result<Value, Value> {
661 let (locale, style) = if let Value::Object(o) = &this {
662 let l = o.borrow().props.get("_locale").cloned().unwrap_or(Value::Str(Rc::new("en-US".into()))).to_js_string();
663 let s = o.borrow().props.get("_style").cloned().unwrap_or(Value::Str(Rc::new("long".into()))).to_js_string();
664 (l, s)
665 } else {
666 (String::from("en-US"), String::from("long"))
667 };
668 let res = Obj::plain();
669 res.borrow_mut().props.insert("locale".into(), Value::Str(Rc::new(locale)));
670 res.borrow_mut().props.insert("style".into(), Value::Str(Rc::new(style)));
671 Ok(Value::Object(res))
672}