From 5a5631faa8cdc04bc90e743826d4acbf66436c35 Mon Sep 17 00:00:00 2001 From: Norbert Garfield Date: Sat, 7 May 2022 10:40:57 +0000 Subject: [PATCH 1/5] DateTimeFormat helpers * toDateTimeOptions * DateTimeStyleFormat * GetOption * GetNumberOption * DefaultNumberOption --- .../src/builtins/intl/date_time_format.rs | 392 +++++++++++++ boa_engine/src/builtins/intl/mod.rs | 138 +++++ boa_engine/src/builtins/intl/tests.rs | 520 +++++++++++++++++- 3 files changed, 1049 insertions(+), 1 deletion(-) diff --git a/boa_engine/src/builtins/intl/date_time_format.rs b/boa_engine/src/builtins/intl/date_time_format.rs index b9ebee9e6e9..da285aceeb4 100644 --- a/boa_engine/src/builtins/intl/date_time_format.rs +++ b/boa_engine/src/builtins/intl/date_time_format.rs @@ -19,6 +19,9 @@ use crate::{ use boa_gc::{Finalize, Trace}; use boa_profiler::Profiler; +#[cfg(test)] +use rustc_hash::FxHashMap; + /// JavaScript `Intl.DateTimeFormat` object. #[derive(Debug, Clone, Trace, Finalize)] pub struct DateTimeFormat { @@ -114,3 +117,392 @@ impl DateTimeFormat { Ok(date_time_format.into()) } } + +/// The abstract operation `toDateTimeOptions` is called with arguments `options`, `required` and +/// `defaults`. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// +/// [spec]: https://tc39.es/ecma402/#sec-todatetimeoptions +#[cfg(test)] +pub(crate) fn to_date_time_options( + options: &JsValue, + required: &str, + defaults: &str, + context: &mut Context, +) -> JsResult { + // 1. If options is undefined, let options be null; + // otherwise let options be ? ToObject(options). + let maybe_options = if options.is_undefined() { + Ok(JsObject::empty()) + } else { + options.to_object(context) + }; + let options = maybe_options.unwrap_or_else(|_| JsObject::empty()); + + // 2. Let options be ! OrdinaryObjectCreate(options). + let options = JsObject::from_proto_and_data(options, ObjectData::ordinary()); + + // 3. Let needDefaults be true. + let mut need_defaults = true; + + // 4. If required is "date" or "any", then + if required.eq("date") || required.eq("any") { + // a. For each property name prop of « "weekday", "year", "month", "day" », do + let property_names = vec!["weekday", "year", "month", "day"]; + // i. Let value be ? Get(options, prop). + // ii. If value is not undefined, let needDefaults be false. + need_defaults = property_names.iter().all(|prop_name| { + options + .get(*prop_name, context) + .unwrap_or_else(|_| JsValue::undefined()) + .is_undefined() + }); + } + + // 5. If required is "time" or "any", then + if required.eq("time") || required.eq("any") { + // a. For each property name prop of « "dayPeriod", "hour", "minute", "second", + // "fractionalSecondDigits" », do + let property_names = vec![ + "dayPeriod", + "hour", + "minute", + "second", + "fractionalSecondDigits", + ]; + // i. Let value be ? Get(options, prop). + // ii. If value is not undefined, let needDefaults be false. + need_defaults = property_names.iter().all(|prop_name| { + options + .get(*prop_name, context) + .unwrap_or_else(|_| JsValue::undefined()) + .is_undefined() + }); + } + + // 6. Let dateStyle be ? Get(options, "dateStyle"). + let date_style = options + .get("dateStyle", context) + .unwrap_or_else(|_| JsValue::undefined()); + + // 7. Let timeStyle be ? Get(options, "timeStyle"). + let time_style = options + .get("timeStyle", context) + .unwrap_or_else(|_| JsValue::undefined()); + + // 8. If dateStyle is not undefined or timeStyle is not undefined, let needDefaults be false. + if !date_style.is_undefined() || !time_style.is_undefined() { + need_defaults = false; + } + + // 9. If required is "date" and timeStyle is not undefined, then + if required.eq("date") && !time_style.is_undefined() { + // a. Throw a TypeError exception. + return context.throw_type_error("'date' is required, but timeStyle was defined"); + } + + // 10. If required is "time" and dateStyle is not undefined, then + if required.eq("time") && !date_style.is_undefined() { + // a. Throw a TypeError exception. + return context.throw_type_error("'time' is required, but dateStyle was defined"); + } + + // 11. If needDefaults is true and defaults is either "date" or "all", then + if need_defaults && (defaults.eq("date") || defaults.eq("all")) { + // a. For each property name prop of « "year", "month", "day" », do + let property_names = vec!["year", "month", "day"]; + // i. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric"). + for prop_name in property_names { + options + .create_data_property_or_throw(prop_name, "numeric", context) + .expect("CreateDataPropertyOrThrow must not fail"); + } + } + + // 12. If needDefaults is true and defaults is either "time" or "all", then + if need_defaults && (defaults.eq("time") || defaults.eq("all")) { + // a. For each property name prop of « "hour", "minute", "second" », do + let property_names = vec!["hour", "minute", "second"]; + // i. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric"). + for prop_name in property_names { + options + .create_data_property_or_throw(prop_name, "numeric", context) + .expect("CreateDataPropertyOrThrow must not fail"); + } + } + + // 13. Return options. + Ok(options) +} + +/// `DateTimeRangeFormat` type contains `rangePatterns` record represented as an object and may or +/// may not contain `rangePatterns12`, thus this field is declared optional. +#[cfg(test)] +pub(crate) struct DateTimeRangeFormat { + pub(crate) range_patterns: JsObject, + pub(crate) range_patterns12: Option, +} + +/// `StylesRecord` type hash maps for `TimeFormat`, `DateFormat`, `DateTimeFormat` and +/// `DateTimeRangeFormat`. The key of these maps should be one of the following: +/// "full", "long", "medium" or "short". Map values contain formatting patterns like +/// "{year}-{month}-{day}" +#[cfg(test)] +pub(crate) struct StylesRecord { + pub(crate) time_format: FxHashMap, + pub(crate) date_format: FxHashMap, + pub(crate) date_time_format: FxHashMap, + pub(crate) date_time_range_format: + FxHashMap>, +} + +/// The `DateTimeStyleFormat` abstract operation accepts arguments `dateStyle` and `timeStyle`, +/// which are each either undefined, "full", "long", "medium", or "short", at least one of which +/// is not undefined, and `styles`, which is a record from +/// %`DateTimeFormat`%.[[`LocaleData`]].[[]].[[styles]].[[]] for some locale `locale` +/// and calendar `calendar`. It returns the appropriate format record for date time formatting +/// based on the parameters. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// +/// [spec]: https://tc39.es/ecma402/#sec-date-time-style-format +#[cfg(test)] +pub(crate) fn date_time_style_format( + date_style: &JsValue, + time_style: &JsValue, + styles: &StylesRecord, + context: &mut Context, +) -> JsResult { + // 1. If timeStyle is not undefined, then + let time_format = if time_style.is_undefined() { + JsValue::undefined() + } else { + // a. Assert: timeStyle is one of "full", "long", "medium", or "short". + let available_time_styles = vec![ + JsValue::String(JsString::new("full")), + JsValue::String(JsString::new("long")), + JsValue::String(JsString::new("medium")), + JsValue::String(JsString::new("short")), + ]; + + if !available_time_styles + .iter() + .any(|style| style.eq(time_style)) + { + return context.throw_type_error("DateTimeStyleFormat: unsupported time style"); + } + + // b. Let timeFormat be styles.[[TimeFormat]].[[]]. + let time_style_str = time_style + .to_string(context) + .unwrap_or_else(|_| JsString::empty()); + let time_fmt = styles + .time_format + .get(&time_style_str) + .expect("Failed to get timeStyle from TimeFormat"); + time_fmt.clone() + }; + + // 2. If dateStyle is not undefined, then + let date_format = if date_style.is_undefined() { + JsValue::undefined() + } else { + // a. Assert: dateStyle is one of "full", "long", "medium", or "short". + let available_date_styles = vec![ + JsValue::String(JsString::new("full")), + JsValue::String(JsString::new("long")), + JsValue::String(JsString::new("medium")), + JsValue::String(JsString::new("short")), + ]; + + if !available_date_styles + .iter() + .any(|style| style.eq(date_style)) + { + return context.throw_type_error("DateTimeStyleFormat: unsupported date style"); + } + + // b. Let dateFormat be styles.[[DateFormat]].[[]]. + let date_style_str = date_style + .to_string(context) + .unwrap_or_else(|_| JsString::empty()); + let date_fmt = styles + .date_format + .get(&date_style_str) + .expect("Failed to get dateStyle from DateFormat"); + date_fmt.clone() + }; + + // 3. If dateStyle is not undefined and timeStyle is not undefined, then + if !date_style.is_undefined() && !time_style.is_undefined() { + // a. Let format be a new Record. + let format = JsObject::empty(); + + // b. Add to format all fields from dateFormat except [[pattern]] and [[rangePatterns]]. + let date_format_obj = date_format + .to_object(context) + .expect("Failed to cast dateFormat to object"); + let entries_list = date_format_obj.enumerable_own_property_names( + crate::property::PropertyNameKind::KeyAndValue, + context, + )?; + + for entry in entries_list { + let entry_obj = entry.to_object(context)?; + let entry_key = entry_obj.get(0, context)?; + let entry_key_str = entry_key.to_string(context)?; + if entry_key_str.ne(&JsString::new("pattern")) + && entry_key_str.ne(&JsString::new("rangePatterns")) + { + let entry_val = entry_obj.get(1, context)?; + format.set(entry_key_str, entry_val, true, context)?; + } + } + + // c. Add to format all fields from timeFormat except + // [[pattern]], [[rangePatterns]], [[pattern12]], and [[rangePatterns12]], if present. + let time_format_obj = time_format + .to_object(context) + .expect("Failed to cast timeFormat to object"); + let entries_list = time_format_obj.enumerable_own_property_names( + crate::property::PropertyNameKind::KeyAndValue, + context, + )?; + for entry in entries_list { + let entry_obj = entry.to_object(context)?; + let entry_key = entry_obj.get(0, context)?; + let entry_key_str = entry_key.to_string(context)?; + if entry_key_str.ne(&JsString::new("pattern")) + && entry_key_str.ne(&JsString::new("rangePatterns")) + && entry_key_str.ne(&JsString::new("pattern12")) + && entry_key_str.ne(&JsString::new("rangePatterns12")) + { + let entry_val = entry_obj.get(1, context)?; + format.set(entry_key_str, entry_val, true, context)?; + } + } + + // d. Let connector be styles.[[DateTimeFormat]].[[]]. + let date_style_str = date_style + .to_string(context) + .unwrap_or_else(|_| JsString::empty()); + let connector = styles + .date_time_format + .get(&date_style_str) + .expect("Failed to get connector"); + let connector_str = connector + .to_string(context) + .expect("Failed to cast connector to string"); + + // e. Let pattern be the string connector with the substring "{0}" replaced with + // timeFormat.[[pattern]] and the substring "{1}" replaced with dateFormat.[[pattern]]. + let time_format_pattern = time_format_obj + .get("pattern", context) + .expect("Failed to get pattern"); + let time_format_pattern = time_format_pattern + .to_string(context) + .expect("Failed to cast pattern to string"); + let time_format_pattern = time_format_pattern.to_string(); + + let date_format_pattern = date_format_obj + .get("pattern", context) + .expect("Failed to get pattern"); + let date_format_pattern = date_format_pattern + .to_string(context) + .expect("Failed to cast pattern to string"); + let date_format_pattern = date_format_pattern.to_string(); + + let pattern = connector_str.replace("{0}", &time_format_pattern); + let pattern = pattern.replace("{1}", &date_format_pattern); + + // f. Set format.[[pattern]] to pattern. + format.set( + "pattern", + JsValue::String(JsString::new(pattern)), + true, + context, + )?; + + // g. If timeFormat has a [[pattern12]] field, then + let maybe_pattern12 = time_format_obj + .get("pattern12", context) + .unwrap_or_else(|_| JsValue::undefined()); + if !maybe_pattern12.is_undefined() { + // i. Let pattern12 be the string connector with the substring "{0}" + // replaced with timeFormat.[[pattern12]] and the substring "{1}" replaced with + // dateFormat.[[pattern]]. + let pattern12_str = maybe_pattern12 + .to_string(context) + .unwrap_or_else(|_| JsString::empty()); + let pattern12_str = pattern12_str.to_string(); + + let date_format_pattern = date_format_obj + .get("pattern", context) + .expect("Failed to get pattern"); + let date_format_pattern = date_format_pattern + .to_string(context) + .expect("Failed to cast pattern to string"); + let date_format_pattern = date_format_pattern.to_string(); + + let pattern12 = connector_str.replace("{0}", &pattern12_str); + let pattern12 = pattern12.replace("{1}", &date_format_pattern); + + // ii. Set format.[[pattern12]] to pattern12. + format.set( + "pattern12", + JsValue::String(JsString::new(pattern12)), + true, + context, + )?; + } + + // h. Let dateTimeRangeFormat be styles.[[DateTimeRangeFormat]].[[]].[[]]. + let date_style_str = date_style + .to_string(context) + .unwrap_or_else(|_| JsString::empty()); + let time_style_str = time_style + .to_string(context) + .unwrap_or_else(|_| JsString::empty()); + let dtr_fmt_date_style = styles + .date_time_range_format + .get(&date_style_str) + .expect("Failed to get dateStyle"); + let date_time_range_format = dtr_fmt_date_style + .get(&time_style_str) + .expect("Failed to get timeStyle"); + + // i. Set format.[[rangePatterns]] to dateTimeRangeFormat.[[rangePatterns]]. + format.set( + "rangePatterns", + date_time_range_format.range_patterns.clone(), + true, + context, + )?; + + // j. If dateTimeRangeFormat has a [[rangePatterns12]] field, then + if let Some(range_patterns12) = &date_time_range_format.range_patterns12 { + // i. Set format.[[rangePatterns12]] to dateTimeRangeFormat.[[rangePatterns12]]. + format.set("rangePatterns12", range_patterns12.clone(), true, context)?; + } + + // k. Return format. + return Ok(JsValue::Object(format)); + } + + // 4. If timeStyle is not undefined, then + if !time_style.is_undefined() { + // a. Return timeFormat. + return Ok(time_format); + } + + // 5. Assert: dateStyle is not undefined. + if date_style.is_undefined() { + return context + .throw_type_error("DateTimeStyleFormat: date style must be defined at this point."); + } + + Ok(date_format) +} diff --git a/boa_engine/src/builtins/intl/mod.rs b/boa_engine/src/builtins/intl/mod.rs index 2c2472122cf..977b7e7fed4 100644 --- a/boa_engine/src/builtins/intl/mod.rs +++ b/boa_engine/src/builtins/intl/mod.rs @@ -653,3 +653,141 @@ fn resolve_locale( // 12. Return result. result } + +/// The abstract operation `GetOption` extracts the value of the property named `property` from the +/// provided `options` object, converts it to the required `type`, checks whether it is one of a +/// `List` of allowed `values`, and fills in a `fallback` value if necessary. If `values` is +/// undefined, there is no fixed set of values and any is permitted. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// +/// [spec]: https://tc39.es/ecma402/#sec-getoption +#[cfg(test)] +pub(crate) fn get_option( + options: &JsValue, + property: &str, + r#type: &str, + values: &[JsValue], + fallback: &JsValue, + context: &mut Context, +) -> JsResult { + // 1. Assert: Type(options) is Object. + if !options.is_object() { + return context.throw_type_error("GetOption: options should be an Object"); + } + + let options_obj = options + .to_object(context) + .expect("GetOption: options should be an Object"); + + // 2. Let value be ? Get(options, property). + let mut value = options_obj + .get(property, context) + .unwrap_or_else(|_| JsValue::undefined()); + + // 3. If value is undefined, return fallback. + if value.is_undefined() { + return Ok(fallback.clone()); + } + + // 4. Assert: type is "boolean" or "string". + if r#type.ne("boolean") && r#type.ne("string") { + return context.throw_type_error("GetOption: type should be either 'boolean' or 'string'"); + } + + // 5. If type is "boolean", then + if r#type.eq("boolean") { + // a. Set value to ! ToBoolean(value). + value = JsValue::Boolean(value.to_boolean()); + } + + // 6. If type is "string", then + if r#type.eq("string") { + // a. Set value to ? ToString(value). + value = JsValue::String( + value + .to_string(context) + .expect("GetOption: failed to convert value to string"), + ); + } + + // 7. If values is not undefined and values does not contain an element equal to value, + // throw a RangeError exception. + if !values.is_empty() && !values.contains(&value) { + return context.throw_range_error("GetOption: values array does not contain value"); + } + + // 8. Return value. + Ok(value) +} + +/// The abstract operation `GetNumberOption` extracts the value of the property named `property` +/// from the provided `options` object, converts it to a `Number value`, checks whether it is in +/// the allowed range, and fills in a `fallback` value if necessary. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// +/// [spec]: https://tc39.es/ecma402/#sec-getnumberoption +#[cfg(test)] +pub(crate) fn get_number_option( + options: &JsValue, + property: &str, + minimum: &JsValue, + maximum: &JsValue, + fallback: &JsValue, + context: &mut Context, +) -> JsResult { + // FIXME untested + // 1. Assert: Type(options) is Object. + if !options.is_object() { + return context.throw_type_error("GetOption: options should be an Object"); + } + + let options_obj = options + .to_object(context) + .expect("GetOption: options should be an Object"); + + // 2. Let value be ? Get(options, property). + let value = options_obj + .get(property, context) + .unwrap_or_else(|_| JsValue::undefined()); + + // 3. Return ? DefaultNumberOption(value, minimum, maximum, fallback). + default_number_option(&value, minimum, maximum, fallback, context) +} + +/// The abstract operation `DefaultNumberOption` converts `value` to a `Number value`, checks +/// whether it is in the allowed range, and fills in a `fallback` value if necessary. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// +/// [spec]: https://tc39.es/ecma402/#sec-defaultnumberoption +#[cfg(test)] +pub(crate) fn default_number_option( + value: &JsValue, + minimum: &JsValue, + maximum: &JsValue, + fallback: &JsValue, + context: &mut Context, +) -> JsResult { + // 1. If value is undefined, return fallback. + if value.is_undefined() { + return Ok(fallback.clone()); + } + + // 2. Set value to ? ToNumber(value). + let value = value.to_number(context)?; + let minimum = minimum.to_number(context).unwrap_or(f64::NEG_INFINITY); + let maximum = maximum.to_number(context).unwrap_or(f64::INFINITY); + + // 3. If value is NaN or less than minimum or greater than maximum, throw a RangeError exception. + if value.is_nan() || value.lt(&minimum) || value.gt(&maximum) { + return context.throw_range_error("DefaultNumberOption: value is out of range."); + } + + // 4. Return floor(value). + Ok(JsValue::new(value.floor())) +} diff --git a/boa_engine/src/builtins/intl/tests.rs b/boa_engine/src/builtins/intl/tests.rs index b0a3d8de36e..565a567de8c 100644 --- a/boa_engine/src/builtins/intl/tests.rs +++ b/boa_engine/src/builtins/intl/tests.rs @@ -1,4 +1,4 @@ -use crate::{Context, JsString}; +use crate::{object::JsObject, Context, JsString, JsValue}; use rustc_hash::FxHashMap; @@ -244,3 +244,521 @@ fn locale_resolution() { ); assert_eq!(locale_record.properties.is_empty(), true); } + +#[test] +fn get_opt() { + let mut context = Context::default(); + + let values = Vec::::new(); + let options_obj = JsValue::undefined(); + let get_option_result = crate::builtins::intl::get_option( + &options_obj, + "", + "", + &values, + &JsValue::String(JsString::empty()), + &mut context, + ); + assert_eq!(get_option_result.is_err(), true); + + let values = Vec::::new(); + let fallback = JsValue::String(JsString::new("fallback")); + let options_obj = JsValue::new(JsObject::empty()); + let get_option_result = + crate::builtins::intl::get_option(&options_obj, "", "", &values, &fallback, &mut context) + .expect("GetOption should not fail on fallback test"); + assert_eq!(get_option_result, fallback); + + let values = Vec::::new(); + let fallback = JsValue::String(JsString::new("fallback")); + let options_obj = JsObject::empty(); + let locale_value = JsValue::String(JsString::new("en-US")); + options_obj + .set("Locale", locale_value.clone(), true, &mut context) + .expect("Setting a property should not fail"); + let get_option_result = crate::builtins::intl::get_option( + &JsValue::new(options_obj), + "Locale", + "number", + &values, + &fallback, + &mut context, + ); + assert_eq!(get_option_result.is_err(), true); + + let values = Vec::::new(); + let fallback = JsValue::String(JsString::new("fallback")); + let options_obj = JsObject::empty(); + let locale_value = JsValue::String(JsString::new("en-US")); + options_obj + .set("Locale", locale_value.clone(), true, &mut context) + .expect("Setting a property should not fail"); + let get_option_result = crate::builtins::intl::get_option( + &JsValue::new(options_obj), + "Locale", + "string", + &values, + &fallback, + &mut context, + ) + .expect("GetOption should not fail on string test"); + assert_eq!(get_option_result, locale_value); + + let fallback = JsValue::String(JsString::new("fallback")); + let options_obj = JsObject::empty(); + let locale_value = JsValue::String(JsString::new("en-US")); + let values = vec![locale_value.clone()]; + options_obj + .set("Locale", locale_value.clone(), true, &mut context) + .expect("Setting a property should not fail"); + let get_option_result = crate::builtins::intl::get_option( + &JsValue::new(options_obj), + "Locale", + "string", + &values, + &fallback, + &mut context, + ) + .expect("GetOption should not fail on values test"); + assert_eq!(get_option_result, locale_value); + + let fallback = JsValue::String(JsString::new("fallback")); + let options_obj = JsObject::empty(); + let locale_value = JsValue::String(JsString::new("en-US")); + let other_locale_value = JsValue::String(JsString::new("de-DE")); + let values = vec![other_locale_value]; + options_obj + .set("Locale", locale_value.clone(), true, &mut context) + .expect("Setting a property should not fail"); + let get_option_result = crate::builtins::intl::get_option( + &JsValue::new(options_obj), + "Locale", + "string", + &values, + &fallback, + &mut context, + ); + assert_eq!(get_option_result.is_err(), true); + + let value = JsValue::undefined(); + let minimum = JsValue::new(1); + let maximum = JsValue::new(10); + let fallback = JsValue::new(5); + let get_option_result = crate::builtins::intl::default_number_option( + &value, + &minimum, + &maximum, + &fallback, + &mut context, + ); + assert_eq!(get_option_result, Ok(fallback)); + + let value = JsValue::nan(); + let minimum = JsValue::new(1); + let maximum = JsValue::new(10); + let fallback = JsValue::new(5); + let get_option_result = crate::builtins::intl::default_number_option( + &value, + &minimum, + &maximum, + &fallback, + &mut context, + ); + assert_eq!(get_option_result.is_err(), true); + + let value = JsValue::new(0); + let minimum = JsValue::new(1); + let maximum = JsValue::new(10); + let fallback = JsValue::new(5); + let get_option_result = crate::builtins::intl::default_number_option( + &value, + &minimum, + &maximum, + &fallback, + &mut context, + ); + assert_eq!(get_option_result.is_err(), true); + + let value = JsValue::new(11); + let minimum = JsValue::new(1); + let maximum = JsValue::new(10); + let fallback = JsValue::new(5); + let get_option_result = crate::builtins::intl::default_number_option( + &value, + &minimum, + &maximum, + &fallback, + &mut context, + ); + assert_eq!(get_option_result.is_err(), true); + + let value = JsValue::new(7); + let minimum = JsValue::new(1); + let maximum = JsValue::new(10); + let fallback = JsValue::new(5); + let get_option_result = crate::builtins::intl::default_number_option( + &value, + &minimum, + &maximum, + &fallback, + &mut context, + ); + assert_eq!(get_option_result, Ok(value)); + + let options = JsValue::undefined(); + let property = "fractionalSecondDigits"; + let minimum = JsValue::new(1); + let maximum = JsValue::new(10); + let fallback = JsValue::new(5); + let get_option_result = crate::builtins::intl::get_number_option( + &options, + &property, + &minimum, + &maximum, + &fallback, + &mut context, + ); + assert_eq!(get_option_result.is_err(), true); + + let options = JsValue::Object(JsObject::empty()); + let property = "fractionalSecondDigits"; + let minimum = JsValue::new(1); + let maximum = JsValue::new(10); + let fallback = JsValue::new(5); + let get_option_result = crate::builtins::intl::get_number_option( + &options, + &property, + &minimum, + &maximum, + &fallback, + &mut context, + ); + assert_eq!(get_option_result, Ok(fallback)); + + let options = JsObject::empty(); + let value = JsValue::new(8); + let property = "fractionalSecondDigits"; + options + .set(property, value.clone(), true, &mut context) + .expect("Setting a property should not fail"); + let options = JsValue::Object(options); + let minimum = JsValue::new(1); + let maximum = JsValue::new(10); + let fallback = JsValue::new(5); + let get_option_result = crate::builtins::intl::get_number_option( + &options, + &property, + &minimum, + &maximum, + &fallback, + &mut context, + ); + assert_eq!(get_option_result, Ok(value)); +} + +#[test] +fn to_date_time_opts() { + let mut context = Context::default(); + + let options_obj = JsObject::empty(); + options_obj + .set("timeStyle", JsObject::empty(), true, &mut context) + .expect("Setting a property should not fail"); + let date_time_opts = crate::builtins::intl::date_time_format::to_date_time_options( + &JsValue::new(options_obj), + "date", + "date", + &mut context, + ); + assert_eq!(date_time_opts.is_err(), true); + + let options_obj = JsObject::empty(); + options_obj + .set("dateStyle", JsObject::empty(), true, &mut context) + .expect("Setting a property should not fail"); + let date_time_opts = crate::builtins::intl::date_time_format::to_date_time_options( + &JsValue::new(options_obj), + "time", + "time", + &mut context, + ); + assert_eq!(date_time_opts.is_err(), true); + + let date_time_opts = crate::builtins::intl::date_time_format::to_date_time_options( + &JsValue::undefined(), + "date", + "date", + &mut context, + ) + .expect("toDateTimeOptions should not fail in date test"); + + let numeric_jsstring = JsValue::String(JsString::new("numeric")); + assert_eq!( + date_time_opts.get("year", &mut context), + Ok(numeric_jsstring.clone()) + ); + assert_eq!( + date_time_opts.get("month", &mut context), + Ok(numeric_jsstring.clone()) + ); + assert_eq!( + date_time_opts.get("day", &mut context), + Ok(numeric_jsstring.clone()) + ); + + let date_time_opts = crate::builtins::intl::date_time_format::to_date_time_options( + &JsValue::undefined(), + "time", + "time", + &mut context, + ) + .expect("toDateTimeOptions should not fail in time test"); + + let numeric_jsstring = JsValue::String(JsString::new("numeric")); + assert_eq!( + date_time_opts.get("hour", &mut context), + Ok(numeric_jsstring.clone()) + ); + assert_eq!( + date_time_opts.get("minute", &mut context), + Ok(numeric_jsstring.clone()) + ); + assert_eq!( + date_time_opts.get("second", &mut context), + Ok(numeric_jsstring.clone()) + ); + + let date_time_opts = crate::builtins::intl::date_time_format::to_date_time_options( + &JsValue::undefined(), + "any", + "all", + &mut context, + ) + .expect("toDateTimeOptions should not fail when testing required = 'any'"); + + let numeric_jsstring = JsValue::String(JsString::new("numeric")); + assert_eq!( + date_time_opts.get("year", &mut context), + Ok(numeric_jsstring.clone()) + ); + assert_eq!( + date_time_opts.get("month", &mut context), + Ok(numeric_jsstring.clone()) + ); + assert_eq!( + date_time_opts.get("day", &mut context), + Ok(numeric_jsstring.clone()) + ); + assert_eq!( + date_time_opts.get("hour", &mut context), + Ok(numeric_jsstring.clone()) + ); + assert_eq!( + date_time_opts.get("minute", &mut context), + Ok(numeric_jsstring.clone()) + ); + assert_eq!( + date_time_opts.get("second", &mut context), + Ok(numeric_jsstring.clone()) + ); +} + +#[test] +fn date_time_style_fmt() { + let mut context = Context::default(); + + let date_style = JsValue::undefined(); + let time_style = JsValue::undefined(); + let styles = crate::builtins::intl::date_time_format::StylesRecord { + time_format: FxHashMap::default(), + date_format: FxHashMap::default(), + date_time_format: FxHashMap::default(), + date_time_range_format: FxHashMap::default(), + }; + let format = crate::builtins::intl::date_time_format::date_time_style_format( + &date_style, + &time_style, + &styles, + &mut context, + ); + assert_eq!(format.is_err(), true); + + let date_style = JsValue::String(JsString::new("invalid")); + let time_style = JsValue::undefined(); + let styles = crate::builtins::intl::date_time_format::StylesRecord { + time_format: FxHashMap::default(), + date_format: FxHashMap::default(), + date_time_format: FxHashMap::default(), + date_time_range_format: FxHashMap::default(), + }; + let format = crate::builtins::intl::date_time_format::date_time_style_format( + &date_style, + &time_style, + &styles, + &mut context, + ); + assert_eq!(format.is_err(), true); + + let date_style = JsValue::undefined(); + let time_style = JsValue::String(JsString::new("invalid")); + let styles = crate::builtins::intl::date_time_format::StylesRecord { + time_format: FxHashMap::default(), + date_format: FxHashMap::default(), + date_time_format: FxHashMap::default(), + date_time_range_format: FxHashMap::default(), + }; + let format = crate::builtins::intl::date_time_format::date_time_style_format( + &date_style, + &time_style, + &styles, + &mut context, + ); + assert_eq!(format.is_err(), true); + + let date_style = JsValue::undefined(); + let time_style = JsValue::String(JsString::new("full")); + let time_format_full = JsObject::empty(); + time_format_full + .set( + "pattern", + JsString::new("{hour}:{minute}:{second}"), + true, + &mut context, + ) + .expect("Setting a property should not fail"); + + let mut styles = crate::builtins::intl::date_time_format::StylesRecord { + time_format: FxHashMap::default(), + date_format: FxHashMap::default(), + date_time_format: FxHashMap::default(), + date_time_range_format: FxHashMap::default(), + }; + styles.time_format.insert( + JsString::new("full"), + JsValue::Object(time_format_full.clone()), + ); + + let format = crate::builtins::intl::date_time_format::date_time_style_format( + &date_style, + &time_style, + &styles, + &mut context, + ); + assert_eq!(format, Ok(JsValue::Object(time_format_full))); + + let date_style = JsValue::String(JsString::new("full")); + let time_style = JsValue::undefined(); + let date_format_full = JsObject::empty(); + date_format_full + .set( + "pattern", + JsString::new("{year}-{month}-{day}"), + true, + &mut context, + ) + .expect("Setting a property should not fail"); + + let mut styles = crate::builtins::intl::date_time_format::StylesRecord { + time_format: FxHashMap::default(), + date_format: FxHashMap::default(), + date_time_format: FxHashMap::default(), + date_time_range_format: FxHashMap::default(), + }; + styles.date_format.insert( + JsString::new("full"), + JsValue::Object(date_format_full.clone()), + ); + + let format = crate::builtins::intl::date_time_format::date_time_style_format( + &date_style, + &time_style, + &styles, + &mut context, + ); + assert_eq!(format, Ok(JsValue::Object(date_format_full))); + + let time_style = JsValue::String(JsString::new("full")); + let time_format_full = JsObject::empty(); + time_format_full + .set( + "pattern", + JsString::new("{hour}:{minute}:{second}"), + true, + &mut context, + ) + .expect("Setting a property should not fail"); + let time_fmt = JsObject::empty(); + time_fmt + .set("full", time_format_full.clone(), true, &mut context) + .expect("Setting a property should not fail"); + + let date_style = JsValue::String(JsString::new("full")); + let date_format_full = JsObject::empty(); + date_format_full + .set( + "pattern", + JsString::new("{year}-{month}-{day}"), + true, + &mut context, + ) + .expect("Setting a property should not fail"); + let date_fmt = JsObject::empty(); + date_fmt + .set("full", date_format_full.clone(), true, &mut context) + .expect("Setting a property should not fail"); + + let connector = JsString::new("timeFormat: {0}, dateFormat: {1}"); + let date_time_fmt = JsObject::empty(); + date_time_fmt + .set("full", connector.clone(), true, &mut context) + .expect("Setting a property should not fail"); + + let mut styles = crate::builtins::intl::date_time_format::StylesRecord { + time_format: FxHashMap::default(), + date_format: FxHashMap::default(), + date_time_format: FxHashMap::default(), + date_time_range_format: FxHashMap::default(), + }; + styles.time_format.insert( + JsString::new("full"), + JsValue::Object(time_format_full.clone()), + ); + styles.date_format.insert( + JsString::new("full"), + JsValue::Object(date_format_full.clone()), + ); + styles + .date_time_format + .insert(JsString::new("full"), JsValue::String(connector)); + let range_patterns = JsObject::empty(); + let dtr_fmt = crate::builtins::intl::date_time_format::DateTimeRangeFormat { + range_patterns: range_patterns.clone(), + range_patterns12: None, + }; + let mut dtr_fmt_map = FxHashMap::default(); + dtr_fmt_map.insert(JsString::new("full"), dtr_fmt); + styles + .date_time_range_format + .insert(JsString::new("full"), dtr_fmt_map); + + let format = crate::builtins::intl::date_time_format::date_time_style_format( + &date_style, + &time_style, + &styles, + &mut context, + ) + .expect("DateTimeStyleFormat failure is unexpected"); + + let format_obj = format + .to_object(&mut context) + .expect("Failed to cast DateTimeStyleFormat to object"); + + let pattern_value = JsValue::String(JsString::new( + "timeFormat: {hour}:{minute}:{second}, dateFormat: {year}-{month}-{day}", + )); + assert_eq!(format_obj.get("pattern", &mut context), Ok(pattern_value)); + assert_eq!( + format_obj.get("rangePatterns", &mut context), + Ok(JsValue::Object(range_patterns)) + ); +} From acc2741050e4f1b7f4619d945ad0d20a68cfb99d Mon Sep 17 00:00:00 2001 From: Norbert Garfield Date: Sun, 8 May 2022 11:10:42 +0000 Subject: [PATCH 2/5] Set more strict types to parameters --- .../src/builtins/intl/date_time_format.rs | 80 ++++---- boa_engine/src/builtins/intl/mod.rs | 100 ++++----- boa_engine/src/builtins/intl/tests.rs | 191 +++++++++--------- 3 files changed, 165 insertions(+), 206 deletions(-) diff --git a/boa_engine/src/builtins/intl/date_time_format.rs b/boa_engine/src/builtins/intl/date_time_format.rs index da285aceeb4..837203b0978 100644 --- a/boa_engine/src/builtins/intl/date_time_format.rs +++ b/boa_engine/src/builtins/intl/date_time_format.rs @@ -134,52 +134,50 @@ pub(crate) fn to_date_time_options( ) -> JsResult { // 1. If options is undefined, let options be null; // otherwise let options be ? ToObject(options). - let maybe_options = if options.is_undefined() { - Ok(JsObject::empty()) + // 2. Let options be ! OrdinaryObjectCreate(options). + let options = if options.is_undefined() { + JsObject::from_proto_and_data(None, ObjectData::ordinary()) } else { - options.to_object(context) + let opt = options.to_object(context)?; + JsObject::from_proto_and_data(opt, ObjectData::ordinary()) }; - let options = maybe_options.unwrap_or_else(|_| JsObject::empty()); - - // 2. Let options be ! OrdinaryObjectCreate(options). - let options = JsObject::from_proto_and_data(options, ObjectData::ordinary()); // 3. Let needDefaults be true. let mut need_defaults = true; // 4. If required is "date" or "any", then - if required.eq("date") || required.eq("any") { + if ["date", "any"].contains(&required) { // a. For each property name prop of « "weekday", "year", "month", "day" », do - let property_names = vec!["weekday", "year", "month", "day"]; - // i. Let value be ? Get(options, prop). - // ii. If value is not undefined, let needDefaults be false. - need_defaults = property_names.iter().all(|prop_name| { - options - .get(*prop_name, context) - .unwrap_or_else(|_| JsValue::undefined()) - .is_undefined() - }); + for property in ["weekday", "year", "month", "day"] { + // i. Let value be ? Get(options, prop). + let value = options.get(property, context)?; + + // ii. If value is not undefined, let needDefaults be false. + if !value.is_undefined() { + need_defaults = false; + } + } } // 5. If required is "time" or "any", then - if required.eq("time") || required.eq("any") { + if ["time", "any"].contains(&required) { // a. For each property name prop of « "dayPeriod", "hour", "minute", "second", // "fractionalSecondDigits" », do - let property_names = vec![ + for property in [ "dayPeriod", "hour", "minute", "second", "fractionalSecondDigits", - ]; - // i. Let value be ? Get(options, prop). - // ii. If value is not undefined, let needDefaults be false. - need_defaults = property_names.iter().all(|prop_name| { - options - .get(*prop_name, context) - .unwrap_or_else(|_| JsValue::undefined()) - .is_undefined() - }); + ] { + // i. Let value be ? Get(options, prop). + let value = options.get(property, context)?; + + // ii. If value is not undefined, let needDefaults be false. + if !value.is_undefined() { + need_defaults = false; + } + } } // 6. Let dateStyle be ? Get(options, "dateStyle"). @@ -188,9 +186,7 @@ pub(crate) fn to_date_time_options( .unwrap_or_else(|_| JsValue::undefined()); // 7. Let timeStyle be ? Get(options, "timeStyle"). - let time_style = options - .get("timeStyle", context) - .unwrap_or_else(|_| JsValue::undefined()); + let time_style = options.get("timeStyle", context)?; // 8. If dateStyle is not undefined or timeStyle is not undefined, let needDefaults be false. if !date_style.is_undefined() || !time_style.is_undefined() { @@ -210,26 +206,20 @@ pub(crate) fn to_date_time_options( } // 11. If needDefaults is true and defaults is either "date" or "all", then - if need_defaults && (defaults.eq("date") || defaults.eq("all")) { + if need_defaults && ["date", "all"].contains(&defaults) { // a. For each property name prop of « "year", "month", "day" », do - let property_names = vec!["year", "month", "day"]; - // i. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric"). - for prop_name in property_names { - options - .create_data_property_or_throw(prop_name, "numeric", context) - .expect("CreateDataPropertyOrThrow must not fail"); + for property in ["year", "month", "day"] { + // i. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric"). + options.create_data_property_or_throw(property, "numeric", context)?; } } // 12. If needDefaults is true and defaults is either "time" or "all", then - if need_defaults && (defaults.eq("time") || defaults.eq("all")) { + if need_defaults && ["time", "all"].contains(&defaults) { // a. For each property name prop of « "hour", "minute", "second" », do - let property_names = vec!["hour", "minute", "second"]; - // i. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric"). - for prop_name in property_names { - options - .create_data_property_or_throw(prop_name, "numeric", context) - .expect("CreateDataPropertyOrThrow must not fail"); + for property in ["hour", "minute", "second"] { + // i. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric"). + options.create_data_property_or_throw(property, "numeric", context)?; } } diff --git a/boa_engine/src/builtins/intl/mod.rs b/boa_engine/src/builtins/intl/mod.rs index 977b7e7fed4..d4723131bec 100644 --- a/boa_engine/src/builtins/intl/mod.rs +++ b/boa_engine/src/builtins/intl/mod.rs @@ -16,6 +16,9 @@ use crate::{ Context, JsResult, JsString, JsValue, }; +#[cfg(test)] +use crate::object::JsObject; + pub mod date_time_format; #[cfg(test)] mod tests; @@ -654,6 +657,12 @@ fn resolve_locale( result } +#[cfg(test)] +pub(crate) enum GetOptionType { + String, + Boolean, +} + /// The abstract operation `GetOption` extracts the value of the property named `property` from the /// provided `options` object, converts it to the required `type`, checks whether it is one of a /// `List` of allowed `values`, and fills in a `fallback` value if necessary. If `values` is @@ -665,26 +674,16 @@ fn resolve_locale( /// [spec]: https://tc39.es/ecma402/#sec-getoption #[cfg(test)] pub(crate) fn get_option( - options: &JsValue, + options: &JsObject, property: &str, - r#type: &str, - values: &[JsValue], + r#type: &GetOptionType, + values: &[JsString], fallback: &JsValue, context: &mut Context, ) -> JsResult { // 1. Assert: Type(options) is Object. - if !options.is_object() { - return context.throw_type_error("GetOption: options should be an Object"); - } - - let options_obj = options - .to_object(context) - .expect("GetOption: options should be an Object"); - // 2. Let value be ? Get(options, property). - let mut value = options_obj - .get(property, context) - .unwrap_or_else(|_| JsValue::undefined()); + let mut value = options.get(property, context)?; // 3. If value is undefined, return fallback. if value.is_undefined() { @@ -692,31 +691,22 @@ pub(crate) fn get_option( } // 4. Assert: type is "boolean" or "string". - if r#type.ne("boolean") && r#type.ne("string") { - return context.throw_type_error("GetOption: type should be either 'boolean' or 'string'"); - } - // 5. If type is "boolean", then - if r#type.eq("boolean") { - // a. Set value to ! ToBoolean(value). - value = JsValue::Boolean(value.to_boolean()); - } - + // a. Set value to ! ToBoolean(value). // 6. If type is "string", then - if r#type.eq("string") { - // a. Set value to ? ToString(value). - value = JsValue::String( - value - .to_string(context) - .expect("GetOption: failed to convert value to string"), - ); - } - + // a. Set value to ? ToString(value). // 7. If values is not undefined and values does not contain an element equal to value, // throw a RangeError exception. - if !values.is_empty() && !values.contains(&value) { - return context.throw_range_error("GetOption: values array does not contain value"); - } + value = match r#type { + GetOptionType::Boolean => JsValue::Boolean(value.to_boolean()), + GetOptionType::String => { + let string_value = value.to_string(context)?; + if !values.is_empty() && !values.contains(&string_value) { + return context.throw_range_error("GetOption: values array does not contain value"); + } + JsValue::String(string_value) + } + }; // 8. Return value. Ok(value) @@ -732,27 +722,16 @@ pub(crate) fn get_option( /// [spec]: https://tc39.es/ecma402/#sec-getnumberoption #[cfg(test)] pub(crate) fn get_number_option( - options: &JsValue, + options: &JsObject, property: &str, - minimum: &JsValue, - maximum: &JsValue, - fallback: &JsValue, + minimum: f64, + maximum: f64, + fallback: Option, context: &mut Context, -) -> JsResult { - // FIXME untested +) -> JsResult { // 1. Assert: Type(options) is Object. - if !options.is_object() { - return context.throw_type_error("GetOption: options should be an Object"); - } - - let options_obj = options - .to_object(context) - .expect("GetOption: options should be an Object"); - // 2. Let value be ? Get(options, property). - let value = options_obj - .get(property, context) - .unwrap_or_else(|_| JsValue::undefined()); + let value = options.get(property, context)?; // 3. Return ? DefaultNumberOption(value, minimum, maximum, fallback). default_number_option(&value, minimum, maximum, fallback, context) @@ -768,20 +747,21 @@ pub(crate) fn get_number_option( #[cfg(test)] pub(crate) fn default_number_option( value: &JsValue, - minimum: &JsValue, - maximum: &JsValue, - fallback: &JsValue, + minimum: f64, + maximum: f64, + fallback: Option, context: &mut Context, -) -> JsResult { +) -> JsResult { // 1. If value is undefined, return fallback. if value.is_undefined() { - return Ok(fallback.clone()); + match fallback { + Some(val_f64) => return Ok(val_f64), + None => return context.throw_type_error("DefaultNumberOption: no fallback provided"), + }; } // 2. Set value to ? ToNumber(value). let value = value.to_number(context)?; - let minimum = minimum.to_number(context).unwrap_or(f64::NEG_INFINITY); - let maximum = maximum.to_number(context).unwrap_or(f64::INFINITY); // 3. If value is NaN or less than minimum or greater than maximum, throw a RangeError exception. if value.is_nan() || value.lt(&minimum) || value.gt(&maximum) { @@ -789,5 +769,5 @@ pub(crate) fn default_number_option( } // 4. Return floor(value). - Ok(JsValue::new(value.floor())) + Ok(value.floor()) } diff --git a/boa_engine/src/builtins/intl/tests.rs b/boa_engine/src/builtins/intl/tests.rs index 565a567de8c..7f16ecd663c 100644 --- a/boa_engine/src/builtins/intl/tests.rs +++ b/boa_engine/src/builtins/intl/tests.rs @@ -249,91 +249,92 @@ fn locale_resolution() { fn get_opt() { let mut context = Context::default(); - let values = Vec::::new(); - let options_obj = JsValue::undefined(); + let values = Vec::::new(); + let fallback = JsValue::String(JsString::new("fallback")); + let options_obj = JsObject::empty(); + let option_type = crate::builtins::intl::GetOptionType::String; let get_option_result = crate::builtins::intl::get_option( &options_obj, "", - "", + &option_type, &values, - &JsValue::String(JsString::empty()), + &fallback, &mut context, - ); - assert_eq!(get_option_result.is_err(), true); - - let values = Vec::::new(); - let fallback = JsValue::String(JsString::new("fallback")); - let options_obj = JsValue::new(JsObject::empty()); - let get_option_result = - crate::builtins::intl::get_option(&options_obj, "", "", &values, &fallback, &mut context) - .expect("GetOption should not fail on fallback test"); + ) + .expect("GetOption should not fail on fallback test"); assert_eq!(get_option_result, fallback); - let values = Vec::::new(); + let values = Vec::::new(); let fallback = JsValue::String(JsString::new("fallback")); let options_obj = JsObject::empty(); let locale_value = JsValue::String(JsString::new("en-US")); options_obj .set("Locale", locale_value.clone(), true, &mut context) .expect("Setting a property should not fail"); + let option_type = crate::builtins::intl::GetOptionType::String; let get_option_result = crate::builtins::intl::get_option( - &JsValue::new(options_obj), + &options_obj, "Locale", - "number", + &option_type, &values, &fallback, &mut context, - ); - assert_eq!(get_option_result.is_err(), true); + ) + .expect("GetOption should not fail on string test"); + assert_eq!(get_option_result, locale_value); - let values = Vec::::new(); let fallback = JsValue::String(JsString::new("fallback")); let options_obj = JsObject::empty(); - let locale_value = JsValue::String(JsString::new("en-US")); + let locale_string = JsString::new("en-US"); + let locale_value = JsValue::String(locale_string.clone()); + let values = vec![locale_string]; options_obj .set("Locale", locale_value.clone(), true, &mut context) .expect("Setting a property should not fail"); + let option_type = crate::builtins::intl::GetOptionType::String; let get_option_result = crate::builtins::intl::get_option( - &JsValue::new(options_obj), + &options_obj, "Locale", - "string", + &option_type, &values, &fallback, &mut context, ) - .expect("GetOption should not fail on string test"); + .expect("GetOption should not fail on values test"); assert_eq!(get_option_result, locale_value); - let fallback = JsValue::String(JsString::new("fallback")); + let fallback = JsValue::new(false); let options_obj = JsObject::empty(); - let locale_value = JsValue::String(JsString::new("en-US")); - let values = vec![locale_value.clone()]; + let boolean_value = JsValue::new(true); + let values = Vec::::new(); options_obj - .set("Locale", locale_value.clone(), true, &mut context) + .set("boolean_val", boolean_value.clone(), true, &mut context) .expect("Setting a property should not fail"); + let option_type = crate::builtins::intl::GetOptionType::Boolean; let get_option_result = crate::builtins::intl::get_option( - &JsValue::new(options_obj), - "Locale", - "string", + &options_obj, + "boolean_val", + &option_type, &values, &fallback, &mut context, ) - .expect("GetOption should not fail on values test"); - assert_eq!(get_option_result, locale_value); + .expect("GetOption should not fail on boolean test"); + assert_eq!(get_option_result, boolean_value); let fallback = JsValue::String(JsString::new("fallback")); let options_obj = JsObject::empty(); let locale_value = JsValue::String(JsString::new("en-US")); - let other_locale_value = JsValue::String(JsString::new("de-DE")); - let values = vec![other_locale_value]; + let other_locale_str = JsString::new("de-DE"); + let values = vec![other_locale_str]; options_obj .set("Locale", locale_value.clone(), true, &mut context) .expect("Setting a property should not fail"); + let option_type = crate::builtins::intl::GetOptionType::String; let get_option_result = crate::builtins::intl::get_option( - &JsValue::new(options_obj), + &options_obj, "Locale", - "string", + &option_type, &values, &fallback, &mut context, @@ -341,119 +342,107 @@ fn get_opt() { assert_eq!(get_option_result.is_err(), true); let value = JsValue::undefined(); - let minimum = JsValue::new(1); - let maximum = JsValue::new(10); - let fallback = JsValue::new(5); + let minimum = 1.0; + let maximum = 10.0; + let fallback_val = 5.0; + let fallback = Some(fallback_val); let get_option_result = crate::builtins::intl::default_number_option( &value, - &minimum, - &maximum, - &fallback, + minimum, + maximum, + fallback, &mut context, ); - assert_eq!(get_option_result, Ok(fallback)); + assert_eq!(get_option_result, Ok(fallback_val)); let value = JsValue::nan(); - let minimum = JsValue::new(1); - let maximum = JsValue::new(10); - let fallback = JsValue::new(5); + let minimum = 1.0; + let maximum = 10.0; + let fallback = Some(5.0); let get_option_result = crate::builtins::intl::default_number_option( &value, - &minimum, - &maximum, - &fallback, + minimum, + maximum, + fallback, &mut context, ); assert_eq!(get_option_result.is_err(), true); let value = JsValue::new(0); - let minimum = JsValue::new(1); - let maximum = JsValue::new(10); - let fallback = JsValue::new(5); + let minimum = 1.0; + let maximum = 10.0; + let fallback = Some(5.0); let get_option_result = crate::builtins::intl::default_number_option( &value, - &minimum, - &maximum, - &fallback, + minimum, + maximum, + fallback, &mut context, ); assert_eq!(get_option_result.is_err(), true); let value = JsValue::new(11); - let minimum = JsValue::new(1); - let maximum = JsValue::new(10); - let fallback = JsValue::new(5); + let minimum = 1.0; + let maximum = 10.0; + let fallback = Some(5.0); let get_option_result = crate::builtins::intl::default_number_option( &value, - &minimum, - &maximum, - &fallback, + minimum, + maximum, + fallback, &mut context, ); assert_eq!(get_option_result.is_err(), true); - let value = JsValue::new(7); - let minimum = JsValue::new(1); - let maximum = JsValue::new(10); - let fallback = JsValue::new(5); + let value_f64 = 7.0; + let value = JsValue::new(value_f64); + let minimum = 1.0; + let maximum = 10.0; + let fallback = Some(5.0); let get_option_result = crate::builtins::intl::default_number_option( &value, - &minimum, - &maximum, - &fallback, + minimum, + maximum, + fallback, &mut context, ); - assert_eq!(get_option_result, Ok(value)); + assert_eq!(get_option_result, Ok(value_f64)); - let options = JsValue::undefined(); - let property = "fractionalSecondDigits"; - let minimum = JsValue::new(1); - let maximum = JsValue::new(10); - let fallback = JsValue::new(5); - let get_option_result = crate::builtins::intl::get_number_option( - &options, - &property, - &minimum, - &maximum, - &fallback, - &mut context, - ); - assert_eq!(get_option_result.is_err(), true); - - let options = JsValue::Object(JsObject::empty()); + let options = JsObject::empty(); let property = "fractionalSecondDigits"; - let minimum = JsValue::new(1); - let maximum = JsValue::new(10); - let fallback = JsValue::new(5); + let minimum = 1.0; + let maximum = 10.0; + let fallback_val = 5.0; + let fallback = Some(fallback_val); let get_option_result = crate::builtins::intl::get_number_option( &options, &property, - &minimum, - &maximum, - &fallback, + minimum, + maximum, + fallback, &mut context, ); - assert_eq!(get_option_result, Ok(fallback)); + assert_eq!(get_option_result, Ok(fallback_val)); let options = JsObject::empty(); - let value = JsValue::new(8); + let value_f64 = 8.0; + let value = JsValue::new(value_f64); let property = "fractionalSecondDigits"; options .set(property, value.clone(), true, &mut context) .expect("Setting a property should not fail"); - let options = JsValue::Object(options); - let minimum = JsValue::new(1); - let maximum = JsValue::new(10); - let fallback = JsValue::new(5); + let minimum = 1.0; + let maximum = 10.0; + let fallback = Some(5.0); let get_option_result = crate::builtins::intl::get_number_option( &options, &property, - &minimum, - &maximum, - &fallback, + minimum, + maximum, + fallback, &mut context, ); - assert_eq!(get_option_result, Ok(value)); + assert_eq!(get_option_result, Ok(value_f64)); } #[test] From 8f6f7d4735b68b304cd0971a503fbba51ee6293b Mon Sep 17 00:00:00 2001 From: Norbert Garfield Date: Wed, 11 May 2022 13:12:31 +0000 Subject: [PATCH 3/5] Remove DateTimeStyleFormat implementation --- .../src/builtins/intl/date_time_format.rs | 273 ------------------ boa_engine/src/builtins/intl/mod.rs | 11 +- boa_engine/src/builtins/intl/tests.rs | 208 +------------ 3 files changed, 8 insertions(+), 484 deletions(-) diff --git a/boa_engine/src/builtins/intl/date_time_format.rs b/boa_engine/src/builtins/intl/date_time_format.rs index 837203b0978..a018f8ab97d 100644 --- a/boa_engine/src/builtins/intl/date_time_format.rs +++ b/boa_engine/src/builtins/intl/date_time_format.rs @@ -19,9 +19,6 @@ use crate::{ use boa_gc::{Finalize, Trace}; use boa_profiler::Profiler; -#[cfg(test)] -use rustc_hash::FxHashMap; - /// JavaScript `Intl.DateTimeFormat` object. #[derive(Debug, Clone, Trace, Finalize)] pub struct DateTimeFormat { @@ -226,273 +223,3 @@ pub(crate) fn to_date_time_options( // 13. Return options. Ok(options) } - -/// `DateTimeRangeFormat` type contains `rangePatterns` record represented as an object and may or -/// may not contain `rangePatterns12`, thus this field is declared optional. -#[cfg(test)] -pub(crate) struct DateTimeRangeFormat { - pub(crate) range_patterns: JsObject, - pub(crate) range_patterns12: Option, -} - -/// `StylesRecord` type hash maps for `TimeFormat`, `DateFormat`, `DateTimeFormat` and -/// `DateTimeRangeFormat`. The key of these maps should be one of the following: -/// "full", "long", "medium" or "short". Map values contain formatting patterns like -/// "{year}-{month}-{day}" -#[cfg(test)] -pub(crate) struct StylesRecord { - pub(crate) time_format: FxHashMap, - pub(crate) date_format: FxHashMap, - pub(crate) date_time_format: FxHashMap, - pub(crate) date_time_range_format: - FxHashMap>, -} - -/// The `DateTimeStyleFormat` abstract operation accepts arguments `dateStyle` and `timeStyle`, -/// which are each either undefined, "full", "long", "medium", or "short", at least one of which -/// is not undefined, and `styles`, which is a record from -/// %`DateTimeFormat`%.[[`LocaleData`]].[[]].[[styles]].[[]] for some locale `locale` -/// and calendar `calendar`. It returns the appropriate format record for date time formatting -/// based on the parameters. -/// -/// More information: -/// - [ECMAScript reference][spec] -/// -/// [spec]: https://tc39.es/ecma402/#sec-date-time-style-format -#[cfg(test)] -pub(crate) fn date_time_style_format( - date_style: &JsValue, - time_style: &JsValue, - styles: &StylesRecord, - context: &mut Context, -) -> JsResult { - // 1. If timeStyle is not undefined, then - let time_format = if time_style.is_undefined() { - JsValue::undefined() - } else { - // a. Assert: timeStyle is one of "full", "long", "medium", or "short". - let available_time_styles = vec![ - JsValue::String(JsString::new("full")), - JsValue::String(JsString::new("long")), - JsValue::String(JsString::new("medium")), - JsValue::String(JsString::new("short")), - ]; - - if !available_time_styles - .iter() - .any(|style| style.eq(time_style)) - { - return context.throw_type_error("DateTimeStyleFormat: unsupported time style"); - } - - // b. Let timeFormat be styles.[[TimeFormat]].[[]]. - let time_style_str = time_style - .to_string(context) - .unwrap_or_else(|_| JsString::empty()); - let time_fmt = styles - .time_format - .get(&time_style_str) - .expect("Failed to get timeStyle from TimeFormat"); - time_fmt.clone() - }; - - // 2. If dateStyle is not undefined, then - let date_format = if date_style.is_undefined() { - JsValue::undefined() - } else { - // a. Assert: dateStyle is one of "full", "long", "medium", or "short". - let available_date_styles = vec![ - JsValue::String(JsString::new("full")), - JsValue::String(JsString::new("long")), - JsValue::String(JsString::new("medium")), - JsValue::String(JsString::new("short")), - ]; - - if !available_date_styles - .iter() - .any(|style| style.eq(date_style)) - { - return context.throw_type_error("DateTimeStyleFormat: unsupported date style"); - } - - // b. Let dateFormat be styles.[[DateFormat]].[[]]. - let date_style_str = date_style - .to_string(context) - .unwrap_or_else(|_| JsString::empty()); - let date_fmt = styles - .date_format - .get(&date_style_str) - .expect("Failed to get dateStyle from DateFormat"); - date_fmt.clone() - }; - - // 3. If dateStyle is not undefined and timeStyle is not undefined, then - if !date_style.is_undefined() && !time_style.is_undefined() { - // a. Let format be a new Record. - let format = JsObject::empty(); - - // b. Add to format all fields from dateFormat except [[pattern]] and [[rangePatterns]]. - let date_format_obj = date_format - .to_object(context) - .expect("Failed to cast dateFormat to object"); - let entries_list = date_format_obj.enumerable_own_property_names( - crate::property::PropertyNameKind::KeyAndValue, - context, - )?; - - for entry in entries_list { - let entry_obj = entry.to_object(context)?; - let entry_key = entry_obj.get(0, context)?; - let entry_key_str = entry_key.to_string(context)?; - if entry_key_str.ne(&JsString::new("pattern")) - && entry_key_str.ne(&JsString::new("rangePatterns")) - { - let entry_val = entry_obj.get(1, context)?; - format.set(entry_key_str, entry_val, true, context)?; - } - } - - // c. Add to format all fields from timeFormat except - // [[pattern]], [[rangePatterns]], [[pattern12]], and [[rangePatterns12]], if present. - let time_format_obj = time_format - .to_object(context) - .expect("Failed to cast timeFormat to object"); - let entries_list = time_format_obj.enumerable_own_property_names( - crate::property::PropertyNameKind::KeyAndValue, - context, - )?; - for entry in entries_list { - let entry_obj = entry.to_object(context)?; - let entry_key = entry_obj.get(0, context)?; - let entry_key_str = entry_key.to_string(context)?; - if entry_key_str.ne(&JsString::new("pattern")) - && entry_key_str.ne(&JsString::new("rangePatterns")) - && entry_key_str.ne(&JsString::new("pattern12")) - && entry_key_str.ne(&JsString::new("rangePatterns12")) - { - let entry_val = entry_obj.get(1, context)?; - format.set(entry_key_str, entry_val, true, context)?; - } - } - - // d. Let connector be styles.[[DateTimeFormat]].[[]]. - let date_style_str = date_style - .to_string(context) - .unwrap_or_else(|_| JsString::empty()); - let connector = styles - .date_time_format - .get(&date_style_str) - .expect("Failed to get connector"); - let connector_str = connector - .to_string(context) - .expect("Failed to cast connector to string"); - - // e. Let pattern be the string connector with the substring "{0}" replaced with - // timeFormat.[[pattern]] and the substring "{1}" replaced with dateFormat.[[pattern]]. - let time_format_pattern = time_format_obj - .get("pattern", context) - .expect("Failed to get pattern"); - let time_format_pattern = time_format_pattern - .to_string(context) - .expect("Failed to cast pattern to string"); - let time_format_pattern = time_format_pattern.to_string(); - - let date_format_pattern = date_format_obj - .get("pattern", context) - .expect("Failed to get pattern"); - let date_format_pattern = date_format_pattern - .to_string(context) - .expect("Failed to cast pattern to string"); - let date_format_pattern = date_format_pattern.to_string(); - - let pattern = connector_str.replace("{0}", &time_format_pattern); - let pattern = pattern.replace("{1}", &date_format_pattern); - - // f. Set format.[[pattern]] to pattern. - format.set( - "pattern", - JsValue::String(JsString::new(pattern)), - true, - context, - )?; - - // g. If timeFormat has a [[pattern12]] field, then - let maybe_pattern12 = time_format_obj - .get("pattern12", context) - .unwrap_or_else(|_| JsValue::undefined()); - if !maybe_pattern12.is_undefined() { - // i. Let pattern12 be the string connector with the substring "{0}" - // replaced with timeFormat.[[pattern12]] and the substring "{1}" replaced with - // dateFormat.[[pattern]]. - let pattern12_str = maybe_pattern12 - .to_string(context) - .unwrap_or_else(|_| JsString::empty()); - let pattern12_str = pattern12_str.to_string(); - - let date_format_pattern = date_format_obj - .get("pattern", context) - .expect("Failed to get pattern"); - let date_format_pattern = date_format_pattern - .to_string(context) - .expect("Failed to cast pattern to string"); - let date_format_pattern = date_format_pattern.to_string(); - - let pattern12 = connector_str.replace("{0}", &pattern12_str); - let pattern12 = pattern12.replace("{1}", &date_format_pattern); - - // ii. Set format.[[pattern12]] to pattern12. - format.set( - "pattern12", - JsValue::String(JsString::new(pattern12)), - true, - context, - )?; - } - - // h. Let dateTimeRangeFormat be styles.[[DateTimeRangeFormat]].[[]].[[]]. - let date_style_str = date_style - .to_string(context) - .unwrap_or_else(|_| JsString::empty()); - let time_style_str = time_style - .to_string(context) - .unwrap_or_else(|_| JsString::empty()); - let dtr_fmt_date_style = styles - .date_time_range_format - .get(&date_style_str) - .expect("Failed to get dateStyle"); - let date_time_range_format = dtr_fmt_date_style - .get(&time_style_str) - .expect("Failed to get timeStyle"); - - // i. Set format.[[rangePatterns]] to dateTimeRangeFormat.[[rangePatterns]]. - format.set( - "rangePatterns", - date_time_range_format.range_patterns.clone(), - true, - context, - )?; - - // j. If dateTimeRangeFormat has a [[rangePatterns12]] field, then - if let Some(range_patterns12) = &date_time_range_format.range_patterns12 { - // i. Set format.[[rangePatterns12]] to dateTimeRangeFormat.[[rangePatterns12]]. - format.set("rangePatterns12", range_patterns12.clone(), true, context)?; - } - - // k. Return format. - return Ok(JsValue::Object(format)); - } - - // 4. If timeStyle is not undefined, then - if !time_style.is_undefined() { - // a. Return timeFormat. - return Ok(time_format); - } - - // 5. Assert: dateStyle is not undefined. - if date_style.is_undefined() { - return context - .throw_type_error("DateTimeStyleFormat: date style must be defined at this point."); - } - - Ok(date_format) -} diff --git a/boa_engine/src/builtins/intl/mod.rs b/boa_engine/src/builtins/intl/mod.rs index d4723131bec..fe763b922fc 100644 --- a/boa_engine/src/builtins/intl/mod.rs +++ b/boa_engine/src/builtins/intl/mod.rs @@ -728,7 +728,7 @@ pub(crate) fn get_number_option( maximum: f64, fallback: Option, context: &mut Context, -) -> JsResult { +) -> JsResult> { // 1. Assert: Type(options) is Object. // 2. Let value be ? Get(options, property). let value = options.get(property, context)?; @@ -751,13 +751,10 @@ pub(crate) fn default_number_option( maximum: f64, fallback: Option, context: &mut Context, -) -> JsResult { +) -> JsResult> { // 1. If value is undefined, return fallback. if value.is_undefined() { - match fallback { - Some(val_f64) => return Ok(val_f64), - None => return context.throw_type_error("DefaultNumberOption: no fallback provided"), - }; + return Ok(fallback); } // 2. Set value to ? ToNumber(value). @@ -769,5 +766,5 @@ pub(crate) fn default_number_option( } // 4. Return floor(value). - Ok(value.floor()) + Ok(Some(value.floor())) } diff --git a/boa_engine/src/builtins/intl/tests.rs b/boa_engine/src/builtins/intl/tests.rs index 7f16ecd663c..ed819456184 100644 --- a/boa_engine/src/builtins/intl/tests.rs +++ b/boa_engine/src/builtins/intl/tests.rs @@ -353,7 +353,7 @@ fn get_opt() { fallback, &mut context, ); - assert_eq!(get_option_result, Ok(fallback_val)); + assert_eq!(get_option_result, Ok(fallback)); let value = JsValue::nan(); let minimum = 1.0; @@ -406,7 +406,7 @@ fn get_opt() { fallback, &mut context, ); - assert_eq!(get_option_result, Ok(value_f64)); + assert_eq!(get_option_result, Ok(Some(value_f64))); let options = JsObject::empty(); let property = "fractionalSecondDigits"; @@ -422,7 +422,7 @@ fn get_opt() { fallback, &mut context, ); - assert_eq!(get_option_result, Ok(fallback_val)); + assert_eq!(get_option_result, Ok(fallback)); let options = JsObject::empty(); let value_f64 = 8.0; @@ -442,7 +442,7 @@ fn get_opt() { fallback, &mut context, ); - assert_eq!(get_option_result, Ok(value_f64)); + assert_eq!(get_option_result, Ok(Some(value_f64))); } #[test] @@ -551,203 +551,3 @@ fn to_date_time_opts() { Ok(numeric_jsstring.clone()) ); } - -#[test] -fn date_time_style_fmt() { - let mut context = Context::default(); - - let date_style = JsValue::undefined(); - let time_style = JsValue::undefined(); - let styles = crate::builtins::intl::date_time_format::StylesRecord { - time_format: FxHashMap::default(), - date_format: FxHashMap::default(), - date_time_format: FxHashMap::default(), - date_time_range_format: FxHashMap::default(), - }; - let format = crate::builtins::intl::date_time_format::date_time_style_format( - &date_style, - &time_style, - &styles, - &mut context, - ); - assert_eq!(format.is_err(), true); - - let date_style = JsValue::String(JsString::new("invalid")); - let time_style = JsValue::undefined(); - let styles = crate::builtins::intl::date_time_format::StylesRecord { - time_format: FxHashMap::default(), - date_format: FxHashMap::default(), - date_time_format: FxHashMap::default(), - date_time_range_format: FxHashMap::default(), - }; - let format = crate::builtins::intl::date_time_format::date_time_style_format( - &date_style, - &time_style, - &styles, - &mut context, - ); - assert_eq!(format.is_err(), true); - - let date_style = JsValue::undefined(); - let time_style = JsValue::String(JsString::new("invalid")); - let styles = crate::builtins::intl::date_time_format::StylesRecord { - time_format: FxHashMap::default(), - date_format: FxHashMap::default(), - date_time_format: FxHashMap::default(), - date_time_range_format: FxHashMap::default(), - }; - let format = crate::builtins::intl::date_time_format::date_time_style_format( - &date_style, - &time_style, - &styles, - &mut context, - ); - assert_eq!(format.is_err(), true); - - let date_style = JsValue::undefined(); - let time_style = JsValue::String(JsString::new("full")); - let time_format_full = JsObject::empty(); - time_format_full - .set( - "pattern", - JsString::new("{hour}:{minute}:{second}"), - true, - &mut context, - ) - .expect("Setting a property should not fail"); - - let mut styles = crate::builtins::intl::date_time_format::StylesRecord { - time_format: FxHashMap::default(), - date_format: FxHashMap::default(), - date_time_format: FxHashMap::default(), - date_time_range_format: FxHashMap::default(), - }; - styles.time_format.insert( - JsString::new("full"), - JsValue::Object(time_format_full.clone()), - ); - - let format = crate::builtins::intl::date_time_format::date_time_style_format( - &date_style, - &time_style, - &styles, - &mut context, - ); - assert_eq!(format, Ok(JsValue::Object(time_format_full))); - - let date_style = JsValue::String(JsString::new("full")); - let time_style = JsValue::undefined(); - let date_format_full = JsObject::empty(); - date_format_full - .set( - "pattern", - JsString::new("{year}-{month}-{day}"), - true, - &mut context, - ) - .expect("Setting a property should not fail"); - - let mut styles = crate::builtins::intl::date_time_format::StylesRecord { - time_format: FxHashMap::default(), - date_format: FxHashMap::default(), - date_time_format: FxHashMap::default(), - date_time_range_format: FxHashMap::default(), - }; - styles.date_format.insert( - JsString::new("full"), - JsValue::Object(date_format_full.clone()), - ); - - let format = crate::builtins::intl::date_time_format::date_time_style_format( - &date_style, - &time_style, - &styles, - &mut context, - ); - assert_eq!(format, Ok(JsValue::Object(date_format_full))); - - let time_style = JsValue::String(JsString::new("full")); - let time_format_full = JsObject::empty(); - time_format_full - .set( - "pattern", - JsString::new("{hour}:{minute}:{second}"), - true, - &mut context, - ) - .expect("Setting a property should not fail"); - let time_fmt = JsObject::empty(); - time_fmt - .set("full", time_format_full.clone(), true, &mut context) - .expect("Setting a property should not fail"); - - let date_style = JsValue::String(JsString::new("full")); - let date_format_full = JsObject::empty(); - date_format_full - .set( - "pattern", - JsString::new("{year}-{month}-{day}"), - true, - &mut context, - ) - .expect("Setting a property should not fail"); - let date_fmt = JsObject::empty(); - date_fmt - .set("full", date_format_full.clone(), true, &mut context) - .expect("Setting a property should not fail"); - - let connector = JsString::new("timeFormat: {0}, dateFormat: {1}"); - let date_time_fmt = JsObject::empty(); - date_time_fmt - .set("full", connector.clone(), true, &mut context) - .expect("Setting a property should not fail"); - - let mut styles = crate::builtins::intl::date_time_format::StylesRecord { - time_format: FxHashMap::default(), - date_format: FxHashMap::default(), - date_time_format: FxHashMap::default(), - date_time_range_format: FxHashMap::default(), - }; - styles.time_format.insert( - JsString::new("full"), - JsValue::Object(time_format_full.clone()), - ); - styles.date_format.insert( - JsString::new("full"), - JsValue::Object(date_format_full.clone()), - ); - styles - .date_time_format - .insert(JsString::new("full"), JsValue::String(connector)); - let range_patterns = JsObject::empty(); - let dtr_fmt = crate::builtins::intl::date_time_format::DateTimeRangeFormat { - range_patterns: range_patterns.clone(), - range_patterns12: None, - }; - let mut dtr_fmt_map = FxHashMap::default(); - dtr_fmt_map.insert(JsString::new("full"), dtr_fmt); - styles - .date_time_range_format - .insert(JsString::new("full"), dtr_fmt_map); - - let format = crate::builtins::intl::date_time_format::date_time_style_format( - &date_style, - &time_style, - &styles, - &mut context, - ) - .expect("DateTimeStyleFormat failure is unexpected"); - - let format_obj = format - .to_object(&mut context) - .expect("Failed to cast DateTimeStyleFormat to object"); - - let pattern_value = JsValue::String(JsString::new( - "timeFormat: {hour}:{minute}:{second}, dateFormat: {year}-{month}-{day}", - )); - assert_eq!(format_obj.get("pattern", &mut context), Ok(pattern_value)); - assert_eq!( - format_obj.get("rangePatterns", &mut context), - Ok(JsValue::Object(range_patterns)) - ); -} From bd5287da011d27bb90984b35367a3c52949fa64e Mon Sep 17 00:00:00 2001 From: Norbert Garfield Date: Thu, 12 May 2022 09:03:16 +0000 Subject: [PATCH 4/5] Add enum for toDateTimeOptions arguments --- .../src/builtins/intl/date_time_format.rs | 27 +++++++++++++------ boa_engine/src/builtins/intl/tests.rs | 20 +++++++------- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/boa_engine/src/builtins/intl/date_time_format.rs b/boa_engine/src/builtins/intl/date_time_format.rs index a018f8ab97d..9477e95946a 100644 --- a/boa_engine/src/builtins/intl/date_time_format.rs +++ b/boa_engine/src/builtins/intl/date_time_format.rs @@ -115,6 +115,17 @@ impl DateTimeFormat { } } +/// Enumeration for `required` and `defaults` arguments in `toDateTimeOptions` subroutine +/// `required` can take Date/Time/Any values, `defaults` can take Date/Time/All +/// `Any` and `All` are merged into one `AnyAll` value. +#[cfg(test)] +#[derive(Debug, PartialEq)] +pub(crate) enum DateTimeReqs { + Date, + Time, + AnyAll, +} + /// The abstract operation `toDateTimeOptions` is called with arguments `options`, `required` and /// `defaults`. /// @@ -125,8 +136,8 @@ impl DateTimeFormat { #[cfg(test)] pub(crate) fn to_date_time_options( options: &JsValue, - required: &str, - defaults: &str, + required: &DateTimeReqs, + defaults: &DateTimeReqs, context: &mut Context, ) -> JsResult { // 1. If options is undefined, let options be null; @@ -143,7 +154,7 @@ pub(crate) fn to_date_time_options( let mut need_defaults = true; // 4. If required is "date" or "any", then - if ["date", "any"].contains(&required) { + if [DateTimeReqs::Date, DateTimeReqs::AnyAll].contains(&required) { // a. For each property name prop of « "weekday", "year", "month", "day" », do for property in ["weekday", "year", "month", "day"] { // i. Let value be ? Get(options, prop). @@ -157,7 +168,7 @@ pub(crate) fn to_date_time_options( } // 5. If required is "time" or "any", then - if ["time", "any"].contains(&required) { + if [DateTimeReqs::Time, DateTimeReqs::AnyAll].contains(&required) { // a. For each property name prop of « "dayPeriod", "hour", "minute", "second", // "fractionalSecondDigits" », do for property in [ @@ -191,19 +202,19 @@ pub(crate) fn to_date_time_options( } // 9. If required is "date" and timeStyle is not undefined, then - if required.eq("date") && !time_style.is_undefined() { + if required == &DateTimeReqs::Date && !time_style.is_undefined() { // a. Throw a TypeError exception. return context.throw_type_error("'date' is required, but timeStyle was defined"); } // 10. If required is "time" and dateStyle is not undefined, then - if required.eq("time") && !date_style.is_undefined() { + if required == &DateTimeReqs::Time && !date_style.is_undefined() { // a. Throw a TypeError exception. return context.throw_type_error("'time' is required, but dateStyle was defined"); } // 11. If needDefaults is true and defaults is either "date" or "all", then - if need_defaults && ["date", "all"].contains(&defaults) { + if need_defaults && [DateTimeReqs::Date, DateTimeReqs::AnyAll].contains(&defaults) { // a. For each property name prop of « "year", "month", "day" », do for property in ["year", "month", "day"] { // i. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric"). @@ -212,7 +223,7 @@ pub(crate) fn to_date_time_options( } // 12. If needDefaults is true and defaults is either "time" or "all", then - if need_defaults && ["time", "all"].contains(&defaults) { + if need_defaults && [DateTimeReqs::Time, DateTimeReqs::AnyAll].contains(&defaults) { // a. For each property name prop of « "hour", "minute", "second" », do for property in ["hour", "minute", "second"] { // i. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric"). diff --git a/boa_engine/src/builtins/intl/tests.rs b/boa_engine/src/builtins/intl/tests.rs index ed819456184..a0e8a92ac3a 100644 --- a/boa_engine/src/builtins/intl/tests.rs +++ b/boa_engine/src/builtins/intl/tests.rs @@ -455,8 +455,8 @@ fn to_date_time_opts() { .expect("Setting a property should not fail"); let date_time_opts = crate::builtins::intl::date_time_format::to_date_time_options( &JsValue::new(options_obj), - "date", - "date", + &crate::builtins::intl::date_time_format::DateTimeReqs::Date, + &crate::builtins::intl::date_time_format::DateTimeReqs::Date, &mut context, ); assert_eq!(date_time_opts.is_err(), true); @@ -467,16 +467,16 @@ fn to_date_time_opts() { .expect("Setting a property should not fail"); let date_time_opts = crate::builtins::intl::date_time_format::to_date_time_options( &JsValue::new(options_obj), - "time", - "time", + &crate::builtins::intl::date_time_format::DateTimeReqs::Time, + &crate::builtins::intl::date_time_format::DateTimeReqs::Time, &mut context, ); assert_eq!(date_time_opts.is_err(), true); let date_time_opts = crate::builtins::intl::date_time_format::to_date_time_options( &JsValue::undefined(), - "date", - "date", + &crate::builtins::intl::date_time_format::DateTimeReqs::Date, + &crate::builtins::intl::date_time_format::DateTimeReqs::Date, &mut context, ) .expect("toDateTimeOptions should not fail in date test"); @@ -497,8 +497,8 @@ fn to_date_time_opts() { let date_time_opts = crate::builtins::intl::date_time_format::to_date_time_options( &JsValue::undefined(), - "time", - "time", + &crate::builtins::intl::date_time_format::DateTimeReqs::Time, + &crate::builtins::intl::date_time_format::DateTimeReqs::Time, &mut context, ) .expect("toDateTimeOptions should not fail in time test"); @@ -519,8 +519,8 @@ fn to_date_time_opts() { let date_time_opts = crate::builtins::intl::date_time_format::to_date_time_options( &JsValue::undefined(), - "any", - "all", + &crate::builtins::intl::date_time_format::DateTimeReqs::AnyAll, + &crate::builtins::intl::date_time_format::DateTimeReqs::AnyAll, &mut context, ) .expect("toDateTimeOptions should not fail when testing required = 'any'"); From 051d3c91dceb86dd0b4b0a78a4642c6e243262e6 Mon Sep 17 00:00:00 2001 From: Norbert Garfield Date: Fri, 13 May 2022 21:18:14 +0000 Subject: [PATCH 5/5] Replace cfg(test) with allow(unused) --- .../src/builtins/intl/date_time_format.rs | 30 +-- boa_engine/src/builtins/intl/mod.rs | 15 +- boa_engine/src/builtins/intl/tests.rs | 185 +++++++----------- 3 files changed, 94 insertions(+), 136 deletions(-) diff --git a/boa_engine/src/builtins/intl/date_time_format.rs b/boa_engine/src/builtins/intl/date_time_format.rs index 9477e95946a..333c224bc41 100644 --- a/boa_engine/src/builtins/intl/date_time_format.rs +++ b/boa_engine/src/builtins/intl/date_time_format.rs @@ -115,10 +115,12 @@ impl DateTimeFormat { } } -/// Enumeration for `required` and `defaults` arguments in `toDateTimeOptions` subroutine -/// `required` can take Date/Time/Any values, `defaults` can take Date/Time/All -/// `Any` and `All` are merged into one `AnyAll` value. -#[cfg(test)] +/// Represents the `required` and `defaults` arguments in the abstract operation +/// `toDateTimeOptions`. +/// +/// Since `required` and `defaults` differ only in the `any` and `all` variants, +/// we combine both in a single variant `AnyAll`. +#[allow(unused)] #[derive(Debug, PartialEq)] pub(crate) enum DateTimeReqs { Date, @@ -133,7 +135,7 @@ pub(crate) enum DateTimeReqs { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma402/#sec-todatetimeoptions -#[cfg(test)] +#[allow(unused)] pub(crate) fn to_date_time_options( options: &JsValue, required: &DateTimeReqs, @@ -144,17 +146,17 @@ pub(crate) fn to_date_time_options( // otherwise let options be ? ToObject(options). // 2. Let options be ! OrdinaryObjectCreate(options). let options = if options.is_undefined() { - JsObject::from_proto_and_data(None, ObjectData::ordinary()) + None } else { - let opt = options.to_object(context)?; - JsObject::from_proto_and_data(opt, ObjectData::ordinary()) + Some(options.to_object(context)?) }; + let options = JsObject::from_proto_and_data(options, ObjectData::ordinary()); // 3. Let needDefaults be true. let mut need_defaults = true; // 4. If required is "date" or "any", then - if [DateTimeReqs::Date, DateTimeReqs::AnyAll].contains(&required) { + if [DateTimeReqs::Date, DateTimeReqs::AnyAll].contains(required) { // a. For each property name prop of « "weekday", "year", "month", "day" », do for property in ["weekday", "year", "month", "day"] { // i. Let value be ? Get(options, prop). @@ -168,7 +170,7 @@ pub(crate) fn to_date_time_options( } // 5. If required is "time" or "any", then - if [DateTimeReqs::Time, DateTimeReqs::AnyAll].contains(&required) { + if [DateTimeReqs::Time, DateTimeReqs::AnyAll].contains(required) { // a. For each property name prop of « "dayPeriod", "hour", "minute", "second", // "fractionalSecondDigits" », do for property in [ @@ -189,9 +191,7 @@ pub(crate) fn to_date_time_options( } // 6. Let dateStyle be ? Get(options, "dateStyle"). - let date_style = options - .get("dateStyle", context) - .unwrap_or_else(|_| JsValue::undefined()); + let date_style = options.get("dateStyle", context)?; // 7. Let timeStyle be ? Get(options, "timeStyle"). let time_style = options.get("timeStyle", context)?; @@ -214,7 +214,7 @@ pub(crate) fn to_date_time_options( } // 11. If needDefaults is true and defaults is either "date" or "all", then - if need_defaults && [DateTimeReqs::Date, DateTimeReqs::AnyAll].contains(&defaults) { + if need_defaults && [DateTimeReqs::Date, DateTimeReqs::AnyAll].contains(defaults) { // a. For each property name prop of « "year", "month", "day" », do for property in ["year", "month", "day"] { // i. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric"). @@ -223,7 +223,7 @@ pub(crate) fn to_date_time_options( } // 12. If needDefaults is true and defaults is either "time" or "all", then - if need_defaults && [DateTimeReqs::Time, DateTimeReqs::AnyAll].contains(&defaults) { + if need_defaults && [DateTimeReqs::Time, DateTimeReqs::AnyAll].contains(defaults) { // a. For each property name prop of « "hour", "minute", "second" », do for property in ["hour", "minute", "second"] { // i. Perform ? CreateDataPropertyOrThrow(options, prop, "numeric"). diff --git a/boa_engine/src/builtins/intl/mod.rs b/boa_engine/src/builtins/intl/mod.rs index fe763b922fc..08c7264edcd 100644 --- a/boa_engine/src/builtins/intl/mod.rs +++ b/boa_engine/src/builtins/intl/mod.rs @@ -10,15 +10,12 @@ use crate::{ builtins::intl::date_time_format::DateTimeFormat, builtins::{Array, BuiltIn, JsArgs}, - object::ObjectInitializer, + object::{JsObject, ObjectInitializer}, property::Attribute, symbol::WellKnownSymbols, Context, JsResult, JsString, JsValue, }; -#[cfg(test)] -use crate::object::JsObject; - pub mod date_time_format; #[cfg(test)] mod tests; @@ -657,7 +654,7 @@ fn resolve_locale( result } -#[cfg(test)] +#[allow(unused)] pub(crate) enum GetOptionType { String, Boolean, @@ -672,7 +669,7 @@ pub(crate) enum GetOptionType { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma402/#sec-getoption -#[cfg(test)] +#[allow(unused)] pub(crate) fn get_option( options: &JsObject, property: &str, @@ -720,7 +717,7 @@ pub(crate) fn get_option( /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma402/#sec-getnumberoption -#[cfg(test)] +#[allow(unused)] pub(crate) fn get_number_option( options: &JsObject, property: &str, @@ -744,7 +741,7 @@ pub(crate) fn get_number_option( /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma402/#sec-defaultnumberoption -#[cfg(test)] +#[allow(unused)] pub(crate) fn default_number_option( value: &JsValue, minimum: f64, @@ -761,7 +758,7 @@ pub(crate) fn default_number_option( let value = value.to_number(context)?; // 3. If value is NaN or less than minimum or greater than maximum, throw a RangeError exception. - if value.is_nan() || value.lt(&minimum) || value.gt(&maximum) { + if value.is_nan() || value < minimum || value > maximum { return context.throw_range_error("DefaultNumberOption: value is out of range."); } diff --git a/boa_engine/src/builtins/intl/tests.rs b/boa_engine/src/builtins/intl/tests.rs index a0e8a92ac3a..7a125b108a7 100644 --- a/boa_engine/src/builtins/intl/tests.rs +++ b/boa_engine/src/builtins/intl/tests.rs @@ -1,4 +1,13 @@ -use crate::{object::JsObject, Context, JsString, JsValue}; +use crate::{ + builtins::intl::date_time_format::{to_date_time_options, DateTimeReqs}, + builtins::intl::{ + best_available_locale, best_fit_matcher, default_locale, default_number_option, + get_number_option, get_option, insert_unicode_extension_and_canonicalize, lookup_matcher, + resolve_locale, unicode_extension_components, DateTimeFormatRecord, GetOptionType, + }, + object::JsObject, + Context, JsString, JsValue, +}; use rustc_hash::FxHashMap; @@ -7,14 +16,14 @@ fn best_avail_loc() { let no_extensions_locale = JsString::new("en-US"); let available_locales = Vec::::new(); assert_eq!( - crate::builtins::intl::best_available_locale(&available_locales, &no_extensions_locale,), + best_available_locale(&available_locales, &no_extensions_locale,), None ); let no_extensions_locale = JsString::new("de-DE"); let available_locales = vec![no_extensions_locale.clone()]; assert_eq!( - crate::builtins::intl::best_available_locale(&available_locales, &no_extensions_locale,), + best_available_locale(&available_locales, &no_extensions_locale,), Some(no_extensions_locale) ); @@ -22,7 +31,7 @@ fn best_avail_loc() { let no_extensions_locale = JsString::new(locale_part.clone() + &"-CA".to_string()); let available_locales = vec![JsString::new(locale_part.clone())]; assert_eq!( - crate::builtins::intl::best_available_locale(&available_locales, &no_extensions_locale,), + best_available_locale(&available_locales, &no_extensions_locale,), Some(JsString::new(locale_part)) ); @@ -31,7 +40,7 @@ fn best_avail_loc() { let no_extensions_locale = JsString::new("ja-Kana-JP-t-it-latn-it"); let available_locales = vec![ja_kana_t.clone(), ja_kana.clone()]; assert_eq!( - crate::builtins::intl::best_available_locale(&available_locales, &no_extensions_locale,), + best_available_locale(&available_locales, &no_extensions_locale,), Some(ja_kana) ); } @@ -42,23 +51,23 @@ fn lookup_match() { let available_locales = Vec::::new(); let requested_locales = Vec::::new(); - let matcher = crate::builtins::intl::lookup_matcher(&available_locales, &requested_locales); - assert_eq!(matcher.locale, crate::builtins::intl::default_locale()); + let matcher = lookup_matcher(&available_locales, &requested_locales); + assert_eq!(matcher.locale, default_locale()); assert_eq!(matcher.extension, ""); // available: [de-DE], requested: [] let available_locales = vec![JsString::new("de-DE")]; let requested_locales = Vec::::new(); - let matcher = crate::builtins::intl::lookup_matcher(&available_locales, &requested_locales); - assert_eq!(matcher.locale, crate::builtins::intl::default_locale()); + let matcher = lookup_matcher(&available_locales, &requested_locales); + assert_eq!(matcher.locale, default_locale()); assert_eq!(matcher.extension, ""); // available: [fr-FR], requested: [fr-FR-u-hc-h12] let available_locales = vec![JsString::new("fr-FR")]; let requested_locales = vec![JsString::new("fr-FR-u-hc-h12")]; - let matcher = crate::builtins::intl::lookup_matcher(&available_locales, &requested_locales); + let matcher = lookup_matcher(&available_locales, &requested_locales); assert_eq!(matcher.locale, "fr-FR"); assert_eq!(matcher.extension, "-u-hc-h12"); @@ -66,7 +75,7 @@ fn lookup_match() { let available_locales = vec![JsString::new("es-ES")]; let requested_locales = vec![JsString::new("es-ES")]; - let matcher = crate::builtins::intl::best_fit_matcher(&available_locales, &requested_locales); + let matcher = best_fit_matcher(&available_locales, &requested_locales); assert_eq!(matcher.locale, "es-ES"); assert_eq!(matcher.extension, ""); } @@ -76,21 +85,21 @@ fn insert_unicode_ext() { let locale = JsString::new("hu-HU"); let ext = JsString::empty(); assert_eq!( - crate::builtins::intl::insert_unicode_extension_and_canonicalize(&locale, &ext), + insert_unicode_extension_and_canonicalize(&locale, &ext), locale ); let locale = JsString::new("hu-HU"); let ext = JsString::new("-u-hc-h12"); assert_eq!( - crate::builtins::intl::insert_unicode_extension_and_canonicalize(&locale, &ext), + insert_unicode_extension_and_canonicalize(&locale, &ext), JsString::new("hu-HU-u-hc-h12") ); let locale = JsString::new("hu-HU-x-PRIVATE"); let ext = JsString::new("-u-hc-h12"); assert_eq!( - crate::builtins::intl::insert_unicode_extension_and_canonicalize(&locale, &ext), + insert_unicode_extension_and_canonicalize(&locale, &ext), JsString::new("hu-HU-u-hc-h12-x-PRIVATE") ); } @@ -98,7 +107,7 @@ fn insert_unicode_ext() { #[test] fn uni_ext_comp() { let ext = JsString::new("-u-ca-japanese-hc-h12"); - let components = crate::builtins::intl::unicode_extension_components(&ext); + let components = unicode_extension_components(&ext); assert_eq!(components.attributes.is_empty(), true); assert_eq!(components.keywords.len(), 2); assert_eq!(components.keywords[0].key, "ca"); @@ -107,7 +116,7 @@ fn uni_ext_comp() { assert_eq!(components.keywords[1].value, "h12"); let ext = JsString::new("-u-alias-co-phonebk-ka-shifted"); - let components = crate::builtins::intl::unicode_extension_components(&ext); + let components = unicode_extension_components(&ext); assert_eq!(components.attributes, vec![JsString::new("alias")]); assert_eq!(components.keywords.len(), 2); assert_eq!(components.keywords[0].key, "co"); @@ -116,7 +125,7 @@ fn uni_ext_comp() { assert_eq!(components.keywords[1].value, "shifted"); let ext = JsString::new("-u-ca-buddhist-kk-nu-thai"); - let components = crate::builtins::intl::unicode_extension_components(&ext); + let components = unicode_extension_components(&ext); assert_eq!(components.attributes.is_empty(), true); assert_eq!(components.keywords.len(), 3); assert_eq!(components.keywords[0].key, "ca"); @@ -127,7 +136,7 @@ fn uni_ext_comp() { assert_eq!(components.keywords[2].value, "thai"); let ext = JsString::new("-u-ca-islamic-civil"); - let components = crate::builtins::intl::unicode_extension_components(&ext); + let components = unicode_extension_components(&ext); assert_eq!(components.attributes.is_empty(), true); assert_eq!(components.keywords.len(), 1); assert_eq!(components.keywords[0].key, "ca"); @@ -143,12 +152,12 @@ fn locale_resolution() { let requested_locales = Vec::::new(); let relevant_extension_keys = Vec::::new(); let locale_data = FxHashMap::default(); - let options = crate::builtins::intl::DateTimeFormatRecord { + let options = DateTimeFormatRecord { locale_matcher: JsString::new("lookup"), properties: FxHashMap::default(), }; - let locale_record = crate::builtins::intl::resolve_locale( + let locale_record = resolve_locale( &available_locales, &requested_locales, &options, @@ -156,14 +165,8 @@ fn locale_resolution() { &locale_data, &mut context, ); - assert_eq!( - locale_record.locale, - crate::builtins::intl::default_locale() - ); - assert_eq!( - locale_record.data_locale, - crate::builtins::intl::default_locale() - ); + assert_eq!(locale_record.locale, default_locale()); + assert_eq!(locale_record.data_locale, default_locale()); assert_eq!(locale_record.properties.is_empty(), true); // test best fit @@ -171,12 +174,12 @@ fn locale_resolution() { let requested_locales = Vec::::new(); let relevant_extension_keys = Vec::::new(); let locale_data = FxHashMap::default(); - let options = crate::builtins::intl::DateTimeFormatRecord { + let options = DateTimeFormatRecord { locale_matcher: JsString::new("best-fit"), properties: FxHashMap::default(), }; - let locale_record = crate::builtins::intl::resolve_locale( + let locale_record = resolve_locale( &available_locales, &requested_locales, &options, @@ -184,14 +187,8 @@ fn locale_resolution() { &locale_data, &mut context, ); - assert_eq!( - locale_record.locale, - crate::builtins::intl::default_locale() - ); - assert_eq!( - locale_record.data_locale, - crate::builtins::intl::default_locale() - ); + assert_eq!(locale_record.locale, default_locale()); + assert_eq!(locale_record.data_locale, default_locale()); assert_eq!(locale_record.properties.is_empty(), true); // available: [es-ES], requested: [es-ES] @@ -199,12 +196,12 @@ fn locale_resolution() { let requested_locales = vec![JsString::new("es-ES")]; let relevant_extension_keys = Vec::::new(); let locale_data = FxHashMap::default(); - let options = crate::builtins::intl::DateTimeFormatRecord { + let options = DateTimeFormatRecord { locale_matcher: JsString::new("lookup"), properties: FxHashMap::default(), }; - let locale_record = crate::builtins::intl::resolve_locale( + let locale_record = resolve_locale( &available_locales, &requested_locales, &options, @@ -221,12 +218,12 @@ fn locale_resolution() { let requested_locales = Vec::::new(); let relevant_extension_keys = Vec::::new(); let locale_data = FxHashMap::default(); - let options = crate::builtins::intl::DateTimeFormatRecord { + let options = DateTimeFormatRecord { locale_matcher: JsString::new("lookup"), properties: FxHashMap::default(), }; - let locale_record = crate::builtins::intl::resolve_locale( + let locale_record = resolve_locale( &available_locales, &requested_locales, &options, @@ -234,14 +231,8 @@ fn locale_resolution() { &locale_data, &mut context, ); - assert_eq!( - locale_record.locale, - crate::builtins::intl::default_locale() - ); - assert_eq!( - locale_record.data_locale, - crate::builtins::intl::default_locale() - ); + assert_eq!(locale_record.locale, default_locale()); + assert_eq!(locale_record.data_locale, default_locale()); assert_eq!(locale_record.properties.is_empty(), true); } @@ -252,8 +243,8 @@ fn get_opt() { let values = Vec::::new(); let fallback = JsValue::String(JsString::new("fallback")); let options_obj = JsObject::empty(); - let option_type = crate::builtins::intl::GetOptionType::String; - let get_option_result = crate::builtins::intl::get_option( + let option_type = GetOptionType::String; + let get_option_result = get_option( &options_obj, "", &option_type, @@ -271,8 +262,8 @@ fn get_opt() { options_obj .set("Locale", locale_value.clone(), true, &mut context) .expect("Setting a property should not fail"); - let option_type = crate::builtins::intl::GetOptionType::String; - let get_option_result = crate::builtins::intl::get_option( + let option_type = GetOptionType::String; + let get_option_result = get_option( &options_obj, "Locale", &option_type, @@ -291,8 +282,8 @@ fn get_opt() { options_obj .set("Locale", locale_value.clone(), true, &mut context) .expect("Setting a property should not fail"); - let option_type = crate::builtins::intl::GetOptionType::String; - let get_option_result = crate::builtins::intl::get_option( + let option_type = GetOptionType::String; + let get_option_result = get_option( &options_obj, "Locale", &option_type, @@ -310,8 +301,8 @@ fn get_opt() { options_obj .set("boolean_val", boolean_value.clone(), true, &mut context) .expect("Setting a property should not fail"); - let option_type = crate::builtins::intl::GetOptionType::Boolean; - let get_option_result = crate::builtins::intl::get_option( + let option_type = GetOptionType::Boolean; + let get_option_result = get_option( &options_obj, "boolean_val", &option_type, @@ -330,8 +321,8 @@ fn get_opt() { options_obj .set("Locale", locale_value.clone(), true, &mut context) .expect("Setting a property should not fail"); - let option_type = crate::builtins::intl::GetOptionType::String; - let get_option_result = crate::builtins::intl::get_option( + let option_type = GetOptionType::String; + let get_option_result = get_option( &options_obj, "Locale", &option_type, @@ -346,52 +337,28 @@ fn get_opt() { let maximum = 10.0; let fallback_val = 5.0; let fallback = Some(fallback_val); - let get_option_result = crate::builtins::intl::default_number_option( - &value, - minimum, - maximum, - fallback, - &mut context, - ); + let get_option_result = default_number_option(&value, minimum, maximum, fallback, &mut context); assert_eq!(get_option_result, Ok(fallback)); let value = JsValue::nan(); let minimum = 1.0; let maximum = 10.0; let fallback = Some(5.0); - let get_option_result = crate::builtins::intl::default_number_option( - &value, - minimum, - maximum, - fallback, - &mut context, - ); + let get_option_result = default_number_option(&value, minimum, maximum, fallback, &mut context); assert_eq!(get_option_result.is_err(), true); let value = JsValue::new(0); let minimum = 1.0; let maximum = 10.0; let fallback = Some(5.0); - let get_option_result = crate::builtins::intl::default_number_option( - &value, - minimum, - maximum, - fallback, - &mut context, - ); + let get_option_result = default_number_option(&value, minimum, maximum, fallback, &mut context); assert_eq!(get_option_result.is_err(), true); let value = JsValue::new(11); let minimum = 1.0; let maximum = 10.0; let fallback = Some(5.0); - let get_option_result = crate::builtins::intl::default_number_option( - &value, - minimum, - maximum, - fallback, - &mut context, - ); + let get_option_result = default_number_option(&value, minimum, maximum, fallback, &mut context); assert_eq!(get_option_result.is_err(), true); let value_f64 = 7.0; @@ -399,13 +366,7 @@ fn get_opt() { let minimum = 1.0; let maximum = 10.0; let fallback = Some(5.0); - let get_option_result = crate::builtins::intl::default_number_option( - &value, - minimum, - maximum, - fallback, - &mut context, - ); + let get_option_result = default_number_option(&value, minimum, maximum, fallback, &mut context); assert_eq!(get_option_result, Ok(Some(value_f64))); let options = JsObject::empty(); @@ -414,7 +375,7 @@ fn get_opt() { let maximum = 10.0; let fallback_val = 5.0; let fallback = Some(fallback_val); - let get_option_result = crate::builtins::intl::get_number_option( + let get_option_result = get_number_option( &options, &property, minimum, @@ -434,7 +395,7 @@ fn get_opt() { let minimum = 1.0; let maximum = 10.0; let fallback = Some(5.0); - let get_option_result = crate::builtins::intl::get_number_option( + let get_option_result = get_number_option( &options, &property, minimum, @@ -453,10 +414,10 @@ fn to_date_time_opts() { options_obj .set("timeStyle", JsObject::empty(), true, &mut context) .expect("Setting a property should not fail"); - let date_time_opts = crate::builtins::intl::date_time_format::to_date_time_options( + let date_time_opts = to_date_time_options( &JsValue::new(options_obj), - &crate::builtins::intl::date_time_format::DateTimeReqs::Date, - &crate::builtins::intl::date_time_format::DateTimeReqs::Date, + &DateTimeReqs::Date, + &DateTimeReqs::Date, &mut context, ); assert_eq!(date_time_opts.is_err(), true); @@ -465,18 +426,18 @@ fn to_date_time_opts() { options_obj .set("dateStyle", JsObject::empty(), true, &mut context) .expect("Setting a property should not fail"); - let date_time_opts = crate::builtins::intl::date_time_format::to_date_time_options( + let date_time_opts = to_date_time_options( &JsValue::new(options_obj), - &crate::builtins::intl::date_time_format::DateTimeReqs::Time, - &crate::builtins::intl::date_time_format::DateTimeReqs::Time, + &DateTimeReqs::Time, + &DateTimeReqs::Time, &mut context, ); assert_eq!(date_time_opts.is_err(), true); - let date_time_opts = crate::builtins::intl::date_time_format::to_date_time_options( + let date_time_opts = to_date_time_options( &JsValue::undefined(), - &crate::builtins::intl::date_time_format::DateTimeReqs::Date, - &crate::builtins::intl::date_time_format::DateTimeReqs::Date, + &DateTimeReqs::Date, + &DateTimeReqs::Date, &mut context, ) .expect("toDateTimeOptions should not fail in date test"); @@ -495,10 +456,10 @@ fn to_date_time_opts() { Ok(numeric_jsstring.clone()) ); - let date_time_opts = crate::builtins::intl::date_time_format::to_date_time_options( + let date_time_opts = to_date_time_options( &JsValue::undefined(), - &crate::builtins::intl::date_time_format::DateTimeReqs::Time, - &crate::builtins::intl::date_time_format::DateTimeReqs::Time, + &DateTimeReqs::Time, + &DateTimeReqs::Time, &mut context, ) .expect("toDateTimeOptions should not fail in time test"); @@ -517,10 +478,10 @@ fn to_date_time_opts() { Ok(numeric_jsstring.clone()) ); - let date_time_opts = crate::builtins::intl::date_time_format::to_date_time_options( + let date_time_opts = to_date_time_options( &JsValue::undefined(), - &crate::builtins::intl::date_time_format::DateTimeReqs::AnyAll, - &crate::builtins::intl::date_time_format::DateTimeReqs::AnyAll, + &DateTimeReqs::AnyAll, + &DateTimeReqs::AnyAll, &mut context, ) .expect("toDateTimeOptions should not fail when testing required = 'any'");