diff --git a/executors/rust/1.3/Cargo.lock b/executors/rust/1.3/Cargo.lock index c42c15df..1d91c954 100644 --- a/executors/rust/1.3/Cargo.lock +++ b/executors/rust/1.3/Cargo.lock @@ -204,6 +204,7 @@ dependencies = [ "fixed_decimal", "icu", "icu_datagen", + "icu_provider", "json", "log", "rustc_version_runtime", diff --git a/executors/rust/1.3/Cargo.toml b/executors/rust/1.3/Cargo.toml index a081c298..2d77f603 100644 --- a/executors/rust/1.3/Cargo.toml +++ b/executors/rust/1.3/Cargo.toml @@ -8,12 +8,14 @@ edition = "2021" log = "0.4" env_logger = "0.9.1" +icu_provider = "~1.3" + json = "0.12.4" serde = "1.0.171" serde_json = "1.0.100" rustc_version_runtime = "0.1.*" -icu = { version = "~1.3", features = ["serde", "icu_compactdecimal", "icu_displaynames", "compiled_data"] } +icu = { version = "~1.3", features = ["serde", "icu_compactdecimal", "icu_displaynames", "compiled_data", "icu_relativetime"] } fixed_decimal = "0.5.3" writeable = "0.5.2" diff --git a/executors/rust/1.3/src/main.rs b/executors/rust/1.3/src/main.rs index 511ba1ac..c595fabd 100644 --- a/executors/rust/1.3/src/main.rs +++ b/executors/rust/1.3/src/main.rs @@ -24,6 +24,7 @@ mod likelysubtags; mod listfmt; mod numberfmt; mod pluralrules; +mod relativedatetime_fmt; use collator::run_collation_test; use langnames::run_language_name_test; @@ -31,6 +32,7 @@ use likelysubtags::run_likelysubtags_test; use listfmt::run_list_fmt_test; use numberfmt::run_numberformat_test; use pluralrules::run_plural_rules_test; +use relativedatetime_fmt::run_relativedatetimeformat_test; use serde_json::{json, Value}; use std::collections::HashMap; @@ -114,6 +116,8 @@ fn main() -> io::Result<()> { run_list_fmt_test(&json_info) } else if test_type == "plural_rules" { run_plural_rules_test(&json_info) + } else if test_type == "rdt_fmt" { + run_relativedatetimeformat_test(&json_info) } else { Err(test_type.to_string()) }; diff --git a/executors/rust/1.3/src/relativedatetime_fmt.rs b/executors/rust/1.3/src/relativedatetime_fmt.rs new file mode 100644 index 00000000..77dc37fa --- /dev/null +++ b/executors/rust/1.3/src/relativedatetime_fmt.rs @@ -0,0 +1,201 @@ +// https://docs.rs/icu/1.3.2/icu/relativetime/struct.RelativeTimeFormatter.html + +use fixed_decimal::FixedDecimal; +use icu::locid::Locale; +use icu_provider::DataLocale; + +use icu::relativetime::{RelativeTimeError, RelativeTimeFormatter, RelativeTimeFormatterOptions}; + +use serde::{Deserialize, Serialize}; +use serde_json::{json, Value}; + +#[derive(Deserialize, Serialize, Debug)] +#[serde(rename_all = "camelCase")] +struct RelativeDateTimeFormatterOptions { + style: Option, + numbering_system: Option, +} + +fn get_formatter_from_unit_style( + locale: &DataLocale, + unit: String, + style: String, + options: RelativeTimeFormatterOptions, +) -> Result { + if unit == "year" { + if style == "long" { + RelativeTimeFormatter::try_new_long_year(locale, options) + } else if style == "short" { + RelativeTimeFormatter::try_new_short_year(locale, options) + } else if style == "narrow" { + RelativeTimeFormatter::try_new_narrow_year(locale, options) + } else { + // Assume long + RelativeTimeFormatter::try_new_long_year(locale, options) + } + } else if unit == "quarter" { + if style == "long" { + RelativeTimeFormatter::try_new_long_quarter(locale, options) + } else if style == "short" { + RelativeTimeFormatter::try_new_short_quarter(locale, options) + } else if style == "narrow" { + RelativeTimeFormatter::try_new_narrow_quarter(locale, options) + } else { + // Assume long + RelativeTimeFormatter::try_new_long_quarter(locale, options) + } + } else if unit == "month" { + if style == "long" { + RelativeTimeFormatter::try_new_long_month(locale, options) + } else if style == "short" { + RelativeTimeFormatter::try_new_short_month(locale, options) + } else if style == "narrow" { + RelativeTimeFormatter::try_new_narrow_month(locale, options) + } else { + // Assume long + RelativeTimeFormatter::try_new_long_month(locale, options) + } + } else if unit == "week" { + if style == "long" { + RelativeTimeFormatter::try_new_long_week(locale, options) + } else if style == "short" { + RelativeTimeFormatter::try_new_short_week(locale, options) + } else if style == "narrow" { + RelativeTimeFormatter::try_new_narrow_week(locale, options) + } else { + // Assume long + RelativeTimeFormatter::try_new_long_week(locale, options) + } + } else if unit == "day" { + if style == "long" { + RelativeTimeFormatter::try_new_long_day(locale, options) + } else if style == "short" { + RelativeTimeFormatter::try_new_short_day(locale, options) + } else if style == "narrow" { + RelativeTimeFormatter::try_new_narrow_day(locale, options) + } else { + // Assume long + RelativeTimeFormatter::try_new_long_day(locale, options) + } + } else if unit == "hour" { + if style == "long" { + RelativeTimeFormatter::try_new_long_hour(locale, options) + } else if style == "short" { + RelativeTimeFormatter::try_new_short_hour(locale, options) + } else if style == "narrow" { + RelativeTimeFormatter::try_new_narrow_hour(locale, options) + } else { + // Assume long + RelativeTimeFormatter::try_new_long_hour(locale, options) + } + } else if unit == "minute" { + if style == "long" { + RelativeTimeFormatter::try_new_long_minute(locale, options) + } else if style == "short" { + RelativeTimeFormatter::try_new_short_minute(locale, options) + } else if style == "narrow" { + RelativeTimeFormatter::try_new_narrow_minute(locale, options) + } else { + // Assume long + RelativeTimeFormatter::try_new_long_minute(locale, options) + } + } else if unit == "second" { + if style == "long" { + RelativeTimeFormatter::try_new_long_second(locale, options) + } else if style == "short" { + RelativeTimeFormatter::try_new_short_second(locale, options) + } else if style == "narrow" { + RelativeTimeFormatter::try_new_narrow_second(locale, options) + } else { + // Assume long + RelativeTimeFormatter::try_new_long_second(locale, options) + } + } else { + // An unknown unit! + RelativeTimeFormatter::try_new_narrow_second(locale, options) + } +} + +pub fn run_relativedatetimeformat_test(json_obj: &Value) -> Result { + let label = &json_obj["label"].as_str().unwrap(); + + // If there are unsupported values, return + // "unsupported" rather than an error. + let options = &json_obj["options"]; // This will be an array. + + let option_struct: RelativeDateTimeFormatterOptions = + serde_json::from_str(&options.to_string()).unwrap(); + + let numbering_system_str = &option_struct.numbering_system; + + let locale_json_str: &str = json_obj["locale"].as_str().unwrap(); + let mut locale_str: String = locale_json_str.to_string(); + if numbering_system_str.is_some() { + locale_str = + locale_json_str.to_string() + "-u-nu-" + &numbering_system_str.as_ref().unwrap(); + } + + let lang_id = if let Ok(lc) = locale_str.parse::() { + lc + } else { + return Ok(json!({ + "label": label, + "error_detail": {"locale": locale_str}, + "error_type": "locale problem", + })); + }; + let data_locale = DataLocale::from(lang_id); + + let count_str: &str = json_obj["count"].as_str().unwrap(); + let count = count_str + .parse::() + .map_err(|e| e.to_string())?; + + let unit: &str = json_obj["unit"].as_str().unwrap(); + + let mut style = ""; + if option_struct.style.is_some() { + style = option_struct.style.as_ref().unwrap(); + } + + // Update when ICU4X supports non-Latn numbering systems + // if option_struct.numbering_system.is_some() { + // let numbering_system = option_struct.numbering_system.as_ref().unwrap(); + + // if numbering_system != "latn" { + // return Ok(json!({ + // "error": "Number system not supported", + // "error_msg": numbering_system, + // "label": label, + // "unsupported": "non-Latn numbering system not implemented", + // })); + // } + // } + + // Use unit & style to select the correct constructor. + let relative_time_formatter = get_formatter_from_unit_style( + &data_locale, + unit.to_string(), + style.to_string(), + RelativeTimeFormatterOptions::default(), + ); + + let formatter = match relative_time_formatter { + Ok(formatter) => formatter, + Err(error) => { + return Ok(json!({ + "error": "Cannot create formatter", + "error_msg": error.to_string(), + "label": label, + })); + } + }; + + let formatted_result = formatter.format(count.clone()); + let result_string = formatted_result.to_string(); + Ok(json!({ + "label": label, + "result": result_string, + "actual_options": format!("{data_locale:?}"), + })) +} diff --git a/executors/rust/1.4/Cargo.lock b/executors/rust/1.4/Cargo.lock index 3243c432..d1426a11 100644 --- a/executors/rust/1.4/Cargo.lock +++ b/executors/rust/1.4/Cargo.lock @@ -204,6 +204,7 @@ dependencies = [ "fixed_decimal", "icu", "icu_datagen", + "icu_provider", "json", "log", "rustc_version_runtime", diff --git a/executors/rust/1.4/Cargo.toml b/executors/rust/1.4/Cargo.toml index 1b4e6152..3833b0a5 100644 --- a/executors/rust/1.4/Cargo.toml +++ b/executors/rust/1.4/Cargo.toml @@ -8,12 +8,14 @@ edition = "2021" log = "0.4" env_logger = "0.9.1" +icu_provider = "~1.4" + json = "0.12.4" serde = "1.0.171" serde_json = "1.0.100" rustc_version_runtime = "0.1.*" -icu = { version = "~1.4", features = ["serde", "icu_compactdecimal", "icu_displaynames"] } +icu = { version = "~1.4", features = ["serde", "icu_compactdecimal", "icu_displaynames", "icu_relativetime"] } fixed_decimal = "0.5.3" writeable = "0.5.2" diff --git a/run_config.json b/run_config.json index 23f0306c..a836da1e 100644 --- a/run_config.json +++ b/run_config.json @@ -277,7 +277,8 @@ "lang_names", "likely_subtags", "list_fmt", - "plural_rules" + "plural_rules", + "rdt_fmt" ], "per_execution": 10000 } @@ -297,7 +298,8 @@ "list_fmt", "likely_subtags", "number_fmt", - "plural_rules" + "plural_rules", + "rdt_fmt" ], "per_execution": 10000 }