diff --git a/.github/workflows/run-rust.yml b/.github/workflows/run-rust.yml index 1383411b..a7b0a1a9 100644 --- a/.github/workflows/run-rust.yml +++ b/.github/workflows/run-rust.yml @@ -18,7 +18,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: - toolchain: 1.73 + toolchain: "1.80" components: rustfmt, clippy default: true - uses: actions-rs/cargo@v1 @@ -31,4 +31,8 @@ jobs: with: command: fmt args: --manifest-path executors/rust/${{ matrix.icu4x-version }}/Cargo.toml -- --check + - name: "Run Rustfmt (executor code)" + run: rustfmt executors/rust/src/** --check + - name: "Run Rustfmt (common code)" + run: rustfmt executors/rust/common/** --check diff --git a/executors/rust/1.3/build.rs b/executors/rust/1.3/build.rs index b67eb37f..b74bcc68 100644 --- a/executors/rust/1.3/build.rs +++ b/executors/rust/1.3/build.rs @@ -1,8 +1,4 @@ -// Getting the version of ICU4X - #[path = "../common/print_icu4x_versions_1_3.rs"] mod print_icu4x_versions_1_3; -fn main() { - print_icu4x_versions_1_3::print(); -} +use print_icu4x_versions_1_3::main; diff --git a/executors/rust/1.3/src/collator.rs b/executors/rust/1.3/src/collator.rs deleted file mode 100644 index 3f4c9daa..00000000 --- a/executors/rust/1.3/src/collator.rs +++ /dev/null @@ -1,47 +0,0 @@ -//! Executor provides tests for Collator. - -use serde_json::{json, Value}; - -use core::cmp::Ordering; -use icu::collator::*; -use icu::locid::locale; - -// Function runs comparison using collator -pub fn run_collation_test(json_obj: &Value) -> Result { - // TODO: Handle errors of missing values and failures. - let label = &json_obj["label"].as_str().unwrap(); - let ignore_punctuation: &Option = &json_obj["ignorePunctuation"].as_bool(); - let str1: &str = json_obj["s1"].as_str().unwrap(); - let str2: &str = json_obj["s2"].as_str().unwrap(); - - let mut options = CollatorOptions::new(); - options.strength = Some(Strength::Tertiary); - - // Ignore punctuation only if using shifted test. - if let Some(ip) = ignore_punctuation { - if *ip { - options.alternate_handling = Some(AlternateHandling::Shifted); - } - } - - let collator: Collator = Collator::try_new(&locale!("en").into(), options).unwrap(); - - let comparison = collator.compare(str1, str2); - - let result_string = comparison.is_le(); - - let mut comparison_number: i16 = 0; - if comparison == Ordering::Less { - comparison_number = -1; - } else if comparison == Ordering::Greater { - comparison_number = 1; - } - // TODO: Convert comparison to "<", "=", or ">" - let json_result = json!({ - "label": label, - "result": result_string, - "compare_result": comparison_number, - "actual_options": format!("{options:?}") - }); - Ok(json_result) -} diff --git a/executors/rust/1.3/src/decimalfmt.rs b/executors/rust/1.3/src/decimalfmt.rs deleted file mode 100644 index c33fae72..00000000 --- a/executors/rust/1.3/src/decimalfmt.rs +++ /dev/null @@ -1,38 +0,0 @@ -//! Executor provides tests for DecimalFormat and NumberFormat. - -use serde_json::{json, Value}; - -use icu::decimal::options; -use icu::decimal::FixedDecimalFormatter; - -use icu::locid::Locale; - -// Runs decimal and number formatting given patterns or skeletons. -pub fn _todo(json_obj: &Value) -> Result { - let label = &json_obj["label"].as_str().unwrap(); - - // Default locale if not specified. - let langid: Locale = json_obj - .get("locale") - .map(|locale_name| locale_name.as_str().unwrap().parse().unwrap()) - .unwrap_or_default(); - - let input = &json_obj["input"].as_str().unwrap(); - - let mut options: options::FixedDecimalFormatterOptions = Default::default(); - // TODO: populate option from input json_obj. - // !! A test. More options to consider! - options.grouping_strategy = options::GroupingStrategy::Min2; - - let fdf = FixedDecimalFormatter::try_new(&langid.into(), options) - .expect("Data should load successfully"); - - // Check if the conversion from the string input is OK. - let input_num = input.parse().expect("valid input format"); - let result_string = fdf.format_to_string(&input_num); - - Ok(json!({ - "label": label, - "result": result_string - })) -} diff --git a/executors/rust/1.3/src/displaynames.rs b/executors/rust/1.3/src/displaynames.rs deleted file mode 100644 index d180d558..00000000 --- a/executors/rust/1.3/src/displaynames.rs +++ /dev/null @@ -1,27 +0,0 @@ -//! Executor provides tests for DisplayNames. - -use serde_json::{json, Value}; - -use crate::icu::displaynames::*; -use icu::locid::Locale; - -// Function runs comparison using displaynames -pub fn _todo(json_obj: &Value) -> Result { - let label = &json_obj["label"].as_str().unwrap(); - let _options = &json_obj["options"].as_str().unwrap(); - let input = &json_obj["input"].as_str().unwrap().parse().unwrap(); - - // Default locale if not specified. - let langid: Locale = json_obj - .get("locale") - .map(|locale_name| locale_name.as_str().unwrap().parse().unwrap()) - .unwrap_or_default(); - - let displaynames = - LocaleDisplayNamesFormatter::try_new(&langid.into(), Default::default()).unwrap(); - - Ok(json!({ - "label": label, - "result": displaynames.of(input) - })) -} diff --git a/executors/rust/1.3/src/likelysubtags.rs b/executors/rust/1.3/src/likelysubtags.rs deleted file mode 100644 index 1d340342..00000000 --- a/executors/rust/1.3/src/likelysubtags.rs +++ /dev/null @@ -1,50 +0,0 @@ -//! Provides tests for likely subtags to mimimize and maximize. - -use serde_json::{json, Value}; - -use icu::locid::Locale; -use icu::locid_transform::LocaleExpander; - -// https://docs.rs/icu/latest/icu/locid_transform/ - -// Function runs language names tests -pub fn run_likelysubtags_test(json_obj: &Value) -> Result { - let lc = LocaleExpander::new_extended(); - - let label = &json_obj["label"].as_str().unwrap(); - - let test_option = &json_obj["option"].as_str().unwrap(); - - let locale_str: &str = json_obj["locale"].as_str().unwrap(); - - let mut locale = locale_str.parse::().unwrap(); - - if test_option == &"minimizeFavorScript" { - // This option is not yet supported. - return Ok(json!({ - "label": label, - "error_detail": {"option": test_option}, - "unsupported": test_option, - "error_type": "unsupported", - })); - } else if test_option == &"minimize" || test_option == &"minimizeFavorRegion" { - lc.minimize(&mut locale); - } else if test_option == &"maximize" { - lc.maximize(&mut locale); - } else { - // This option is not yet supported. - return Ok(json!({ - "label": label, - "error_detail": {"option": test_option}, - "unsupported": test_option, - "error_type": "unsupported", - })); - } - - let json_result = json!({ - "label": label, - "result": locale.to_string() - }); - - Ok(json_result) -} diff --git a/executors/rust/1.3/src/listfmt.rs b/executors/rust/1.3/src/listfmt.rs deleted file mode 100644 index ee9227de..00000000 --- a/executors/rust/1.3/src/listfmt.rs +++ /dev/null @@ -1,94 +0,0 @@ -//! Executor provides tests for list formatting in locale-sensitive manner. - -use serde::{Deserialize, Serialize}; -use serde_json::{json, Value}; - -use icu::list::*; -use icu::locid::Locale; - -use writeable::Writeable; - -#[derive(Deserialize, Serialize, Debug)] -#[serde(rename_all = "snake_case")] -struct ListFormatOptions { - style: Option, - list_type: Option, -} - -// Function runs list format tests -pub fn run_list_fmt_test(json_obj: &Value) -> Result { - let label = &json_obj["label"].as_str().unwrap(); - - let locale_str: &str = json_obj["locale"].as_str().unwrap(); - let locale = locale_str.parse::().unwrap(); - - let options = &json_obj["options"]; // This will be an array. - let option_struct: ListFormatOptions = serde_json::from_str(&options.to_string()).unwrap(); - - let style: &str = option_struct.style.as_ref().unwrap(); - let list_type: &str = option_struct.list_type.as_ref().unwrap(); - - // get input list - - // Style - let list_style: ListLength = if style == "long" { - ListLength::Wide - } else if style == "short" { - ListLength::Short - } else if style == "narrow" { - ListLength::Narrow - } else { - // Report an unsupported option. - return Ok(json!({ - "label": label, - "error_detail": {"option": style}, - "unsupported": "unknown list style", - "error_type": "unsupported", - })); - }; - - let list_formatter_result: Result = if list_type == "conjunction" { - // Reference: https://docs.rs/icu/latest/icu/list/index.html - ListFormatter::try_new_and_with_length(&locale.into(), list_style) - } else if list_type == "disjunction" { - ListFormatter::try_new_or_with_length(&locale.into(), list_style) - } else if list_type == "unit" { - ListFormatter::try_new_unit_with_length(&locale.into(), list_style) - } else { - // This option is not supported. - return Ok(json!({ - "label": label, - "error_detail": {"option": list_type}, - "unsupported": "unknown format type", - "error_type": "unsupported", - })); - }; - - // Data to be formatted. - let input_list = json_obj["input_list"].as_array().unwrap(); - let input_list_str_iter = input_list - .iter() - .map(|json_value| json_value.as_str().unwrap()); - - let json_result = match list_formatter_result { - Ok(formatter) => { - json!({ - "label": label, - "result": formatter.format( - input_list_str_iter).write_to_string().into_owned() - }) - } - Err(e) => { - json!({ - "label": label, - "locale_label": locale_str, - "error": e.to_string(), - "error_type": "unsupported", - "unsupported": e.to_string(), - "error_detail": {"unsupported_locale": locale_str} - }) - } - }; - - Ok(json_result) -} diff --git a/executors/rust/1.3/src/main.rs b/executors/rust/1.3/src/main.rs index 0d980f5b..511dc8ff 100644 --- a/executors/rust/1.3/src/main.rs +++ b/executors/rust/1.3/src/main.rs @@ -1,40 +1,4 @@ -// Read JSON test case from standard in. -// Parse to get test and parameters -// Run test -// Return results - -use std::io; - -mod icu { - pub use ::icu::compactdecimal; - pub use ::icu::displaynames; - pub use ::icu::relativetime; -} - -mod collator; -mod datetimefmt; -mod decimalfmt; -mod displaynames; -mod likelysubtags; -mod listfmt; -mod localenames; -mod numberfmt; -mod pluralrules; -mod relativedatetime_fmt; - #[path = "../../common/run_all_tests.rs"] mod run_all_tests; -fn main() -> io::Result<()> { - let executor_fns = run_all_tests::ExecutorFns { - run_collation_test: collator::run_collation_test, - run_datetimeformat_test: datetimefmt::run_datetimeformat_test, - run_likelysubtags_test: likelysubtags::run_likelysubtags_test, - run_list_fmt_test: listfmt::run_list_fmt_test, - run_locale_name_test: localenames::run_locale_name_test, - run_numberformat_test: numberfmt::run_numberformat_test, - run_plural_rules_test: pluralrules::run_plural_rules_test, - run_relativedatetimeformat_test: relativedatetime_fmt::run_relativedatetimeformat_test, - }; - run_all_tests::main(executor_fns) -} +use run_all_tests::main; diff --git a/executors/rust/1.3/src/pluralrules.rs b/executors/rust/1.3/src/pluralrules.rs deleted file mode 100644 index d0e5a6f3..00000000 --- a/executors/rust/1.3/src/pluralrules.rs +++ /dev/null @@ -1,85 +0,0 @@ -use fixed_decimal::FixedDecimal; -use icu::locid::Locale; -use serde_json::{json, Value}; -use std::str::FromStr; - -// https://docs.rs/icu/latest/icu/plurals/index.html -use icu::plurals::{PluralCategory, PluralRuleType, PluralRules}; - -// Function runs plural rules -pub fn run_plural_rules_test(json_obj: &Value) -> Result { - let label = &json_obj["label"].as_str().unwrap(); - - let locale_str: &str = json_obj["locale"].as_str().unwrap(); - - let locale = if let Ok(lc) = locale_str.parse::() { - lc - } else { - return Ok(json!({ - "label": label, - "error_detail": {"option": locale_str}, - "error_type": "locale problem", - })); - }; - - // Get number string - let input_number = &json_obj["sample"].as_str().unwrap(); - - // Returns error if parsing the number string fails. - let test_number = if let Ok(fd) = FixedDecimal::from_str(input_number) { - fd - } else { - // Report an unexpected result. - return Ok(json!({ - "label": label, - "error_detail": {"sample number": input_number}, - "error_type": "parsing sample", - "unsupported": "input number" - })); - }; - - // Get type string: either ordinal or cardinal - let plural_type_string: &str = json_obj["type"].as_str().unwrap(); - - // Get PluralRuleTypecardinal / ordinal - let plural_type = if plural_type_string == "cardinal" { - PluralRuleType::Cardinal - } else { - PluralRuleType::Ordinal - }; - - let pr = PluralRules::try_new(&locale.into(), plural_type).expect("locale should be present"); - - // Get the category and convert to a string. - let category = pr.category_for(&test_number); - - let category_string = if category == PluralCategory::Zero { - "zero" - } else if category == PluralCategory::One { - "one" - } else if category == PluralCategory::Two { - "two" - } else if category == PluralCategory::Few { - "few" - } else if category == PluralCategory::Many { - "many" - } else if category == PluralCategory::Other { - "other" - } else { - // Report an unexpected result. - return Ok(json!({ - "label": label, - "error_detail": {"option": "unknown category returned"}, - "error_type": "unsupported", - "unsupported": "unknown plural result" - })); - }; - - let json_result = json!({ - "label": label, - "result": category_string - }); - - // Return the string - Ok(json_result) -} diff --git a/executors/rust/1.3/src/relativedatetime_fmt.rs b/executors/rust/1.3/src/relativedatetime_fmt.rs deleted file mode 100644 index 91b48904..00000000 --- a/executors/rust/1.3/src/relativedatetime_fmt.rs +++ /dev/null @@ -1,227 +0,0 @@ -// https://docs.rs/icu/1.3.2/icu/relativetime/struct.RelativeTimeFormatter.html -// https://docs.rs/icu_provider/1.3.0/icu_provider/struct.DataLocale.html#method.get_unicode_ext - -use fixed_decimal::FixedDecimal; - -use icu::locid::extensions::unicode; -use icu::locid::extensions::unicode::key; -use icu::locid::Locale; - -use std::str::FromStr; - -use icu_provider::DataLocale; - -use crate::icu::relativetime::options::Numeric; -use crate::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, - numeric: 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 = &option_struct.numbering_system; - - // Set up the locale with its options. - let locale_json_str: &str = json_obj["locale"].as_str().unwrap(); - let locale_str: String = locale_json_str.to_string(); - - let locale_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 mut data_locale = DataLocale::from(locale_id); - - if numbering_system_str.is_some() { - let numbering_system: &String = numbering_system_str.as_ref().unwrap(); - - // Set up the numbering system in the locale. - data_locale.set_unicode_ext( - key!("nu"), - unicode::Value::from_str(numbering_system).unwrap(), - ); - - // TODO: update when ICU4X supports numbering systems. - // Note that "actual_options" returns the expanded locale, - // e.g., "actual_options":"DataLocale{en-US-u-nu-arab}" - if numbering_system != "latn" { - return Ok(json!({ - "error": "Number system not supported", - "error_msg": numbering_system, - "error_detail": {"locale": format!("{data_locale:?}")}, - "label": label, - "unsupported": "non-Latn numbering system", - })); - } - } - - 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(); - } - - // Get numeric option - let options = if option_struct.numeric == Some(String::from("auto")) { - RelativeTimeFormatterOptions { - numeric: Numeric::Auto, - } - } else { - // The default - RelativeTimeFormatterOptions { - numeric: Numeric::Always, - } - }; - - // 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(), options); - - 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/build.rs b/executors/rust/1.4/build.rs index b67eb37f..b74bcc68 100644 --- a/executors/rust/1.4/build.rs +++ b/executors/rust/1.4/build.rs @@ -1,8 +1,4 @@ -// Getting the version of ICU4X - #[path = "../common/print_icu4x_versions_1_3.rs"] mod print_icu4x_versions_1_3; -fn main() { - print_icu4x_versions_1_3::print(); -} +use print_icu4x_versions_1_3::main; diff --git a/executors/rust/1.4/src/main.rs b/executors/rust/1.4/src/main.rs index b1807ccd..511dc8ff 100644 --- a/executors/rust/1.4/src/main.rs +++ b/executors/rust/1.4/src/main.rs @@ -1 +1,4 @@ -include!("../../1.3/src/main.rs"); +#[path = "../../common/run_all_tests.rs"] +mod run_all_tests; + +use run_all_tests::main; diff --git a/executors/rust/1.5/build.rs b/executors/rust/1.5/build.rs index 93e4d560..81baa698 100644 --- a/executors/rust/1.5/build.rs +++ b/executors/rust/1.5/build.rs @@ -1,8 +1,4 @@ -// Getting the version of ICU4X - #[path = "../common/print_icu4x_versions_1_5.rs"] mod print_icu4x_versions_1_5; -fn main() { - print_icu4x_versions_1_5::print(); -} +use print_icu4x_versions_1_5::main; diff --git a/executors/rust/1.5/src/main.rs b/executors/rust/1.5/src/main.rs index f308648a..511dc8ff 100644 --- a/executors/rust/1.5/src/main.rs +++ b/executors/rust/1.5/src/main.rs @@ -1,60 +1,4 @@ -// Read JSON test case from standard in. -// Parse to get test and parameters -// Run test -// Return results - -use std::io; - -#[path = "../../1.3/src/collator.rs"] -mod collator; - -#[path = "../../1.3/src/datetimefmt.rs"] -#[allow(deprecated)] -mod datetimefmt; - -#[path = "../../1.3/src/decimalfmt.rs"] -mod decimalfmt; - -#[path = "../../1.3/src/displaynames.rs"] -mod displaynames; - -#[path = "../../1.3/src/likelysubtags.rs"] -mod likelysubtags; - -#[path = "../../1.3/src/listfmt.rs"] -mod listfmt; - -#[path = "../../1.3/src/localenames.rs"] -mod localenames; - -#[path = "../../1.3/src/numberfmt.rs"] -mod numberfmt; - -#[path = "../../1.3/src/pluralrules.rs"] -mod pluralrules; - -#[path = "../../1.3/src/relativedatetime_fmt.rs"] -mod relativedatetime_fmt; - #[path = "../../common/run_all_tests.rs"] mod run_all_tests; -mod icu { - pub use ::icu::experimental::compactdecimal; - pub use ::icu::experimental::displaynames; - pub use ::icu::experimental::relativetime; -} - -fn main() -> io::Result<()> { - let executor_fns = run_all_tests::ExecutorFns { - run_collation_test: collator::run_collation_test, - run_datetimeformat_test: datetimefmt::run_datetimeformat_test, - run_likelysubtags_test: likelysubtags::run_likelysubtags_test, - run_list_fmt_test: listfmt::run_list_fmt_test, - run_locale_name_test: localenames::run_locale_name_test, - run_numberformat_test: numberfmt::run_numberformat_test, - run_plural_rules_test: pluralrules::run_plural_rules_test, - run_relativedatetimeformat_test: relativedatetime_fmt::run_relativedatetimeformat_test, - }; - run_all_tests::main(executor_fns) -} +use run_all_tests::main; diff --git a/executors/rust/2.0-beta1/build.rs b/executors/rust/2.0-beta1/build.rs index 93e4d560..81baa698 100644 --- a/executors/rust/2.0-beta1/build.rs +++ b/executors/rust/2.0-beta1/build.rs @@ -1,8 +1,4 @@ -// Getting the version of ICU4X - #[path = "../common/print_icu4x_versions_1_5.rs"] mod print_icu4x_versions_1_5; -fn main() { - print_icu4x_versions_1_5::print(); -} +use print_icu4x_versions_1_5::main; diff --git a/executors/rust/2.0-beta1/src/localenames.rs b/executors/rust/2.0-beta1/src/localenames.rs deleted file mode 100644 index dc03aaba..00000000 --- a/executors/rust/2.0-beta1/src/localenames.rs +++ /dev/null @@ -1,106 +0,0 @@ -//! Executor provides tests for LanguageNames, a special case of DisplayNames. - -use serde_json::{json, Value}; - -use icu::experimental::displaynames::{DisplayNamesOptions, LocaleDisplayNamesFormatter}; - -use icu::locale::Locale; - -use icu::experimental::displaynames::LanguageDisplay; - -// Function runs locale names tests -pub fn run_locale_name_test(json_obj: &Value) -> Result { - let label = &json_obj["label"].as_str().unwrap(); - let mut options: DisplayNamesOptions = Default::default(); - - let language_label = json_obj["language_label"] - .as_str() - .unwrap() - .replace('_', "-"); - let input_locale_result = language_label.parse::(); - let input_locale = match input_locale_result { - Ok(l) => l, - Err(_e) => { - return Ok(json!({ - "error": format!("bad language label: {}", language_label), - // ?? Get the string associated? - // "error_msg": e.as_str(), - "label": label, - "language_label": language_label, - "test_type": "display_names", - "unsupported": "language_label", - "error_type": "unsupported", - })); - } - }; - - let locale_name_result = &json_obj["locale_label"].as_str(); - let locale_name = match locale_name_result { - Some(s) => s, - None => { - return Ok(json!({ - "error": "locale label", - "label": label, - "language_label": language_label, - "test_type": "display_names", - "unsupported": "locale name", - "error_type": "unsupported", - "error_detail": {"cannot parse locale": &locale_name_result} - })) - } - }; - - // Get either standard or dialect form of locale name. - let language_display_result = json_obj["languageDisplay"].as_str(); - let language_display: LanguageDisplay = match language_display_result { - Some(s) => match s { - "standard" => LanguageDisplay::Standard, - "dialect" => LanguageDisplay::Dialect, - &_ => LanguageDisplay::Standard, - }, - None => LanguageDisplay::Standard, // The default - }; - - options.language_display = language_display; - - let langid_result = locale_name.parse::(); - - let langid = match langid_result { - Ok(lid) => lid, - Err(e) => { - return Ok(json!({ - "error": e.to_string(), - "label": label, - "locale_label": locale_name, - "language_label": language_label, - "test_type": "display_names", - "unsupported": "locale name", - "error_type": "unsupported", - "error_detail": {"unsupported_locale": locale_name}, - })) - } - }; - - let display_name_formatter = LocaleDisplayNamesFormatter::try_new(langid.into(), options); - - let json_result = match display_name_formatter { - Ok(formatter) => { - json!({ - "label": label, - "result": formatter.of(&input_locale) - }) - } - Err(e) => { - json!({ - "label": label, - "locale_label": locale_name, - "error": e.to_string(), - "error_type": "unsupported", - "unsupported": e.to_string(), - "error_detail": {"formatting fails for": locale_name} - }) - } - }; - - Ok(json_result) -} diff --git a/executors/rust/2.0-beta1/src/main.rs b/executors/rust/2.0-beta1/src/main.rs index 90e179e6..511dc8ff 100644 --- a/executors/rust/2.0-beta1/src/main.rs +++ b/executors/rust/2.0-beta1/src/main.rs @@ -1,48 +1,4 @@ -// Read JSON test case from standard in. -// Parse to get test and parameters -// Run test -// Return results - -use std::io; - -mod collator; -mod datetimefmt; -mod decimalfmt; -mod displaynames; -mod likelysubtags; -mod listfmt; -mod localenames; -mod numberfmt; -mod pluralrules; -mod relativedatetime_fmt; - -#[path = "../../common/try_or_return_error.rs"] -#[macro_use] -mod try_or_return_error; - -#[allow(unused)] -pub fn return_error(json_obj: &serde_json::Value) -> Result { - let label = &json_obj["label"].as_str().unwrap(); - return Ok(serde_json::json!({ - "label": label, - "error_type": "datetime ignored", - "error": "datetime ignored" - })); -} - #[path = "../../common/run_all_tests.rs"] mod run_all_tests; -fn main() -> io::Result<()> { - let executor_fns = run_all_tests::ExecutorFns { - run_collation_test: collator::run_collation_test, - run_datetimeformat_test: datetimefmt::run_datetimeformat_test, - run_likelysubtags_test: likelysubtags::run_likelysubtags_test, - run_list_fmt_test: listfmt::run_list_fmt_test, - run_locale_name_test: localenames::run_locale_name_test, - run_numberformat_test: numberfmt::run_numberformat_test, - run_plural_rules_test: pluralrules::run_plural_rules_test, - run_relativedatetimeformat_test: relativedatetime_fmt::run_relativedatetimeformat_test, - }; - run_all_tests::main(executor_fns) -} +use run_all_tests::main; diff --git a/executors/rust/2.0-beta1/src/numberfmt.rs b/executors/rust/2.0-beta1/src/numberfmt.rs deleted file mode 100644 index f2ffc168..00000000 --- a/executors/rust/2.0-beta1/src/numberfmt.rs +++ /dev/null @@ -1,227 +0,0 @@ -//! Executor provides tests for NumberFormat and DecimalFormat. - -use fixed_decimal::FixedDecimal; -use fixed_decimal::RoundingMode; -use fixed_decimal::SignDisplay; -// TODO: use fixed_decimal::ScientificDecimal; - -use icu::decimal::options; -use icu::decimal::FixedDecimalFormatter; - -use icu::experimental::compactdecimal::CompactDecimalFormatter; - -use icu::locale::{extensions::unicode::key, Locale}; - -use serde::{Deserialize, Serialize}; -use serde_json::{json, Value}; - -use writeable::Writeable; - -// Support options - update when ICU4X adds support -static _SUPPORTED_OPTIONS: [&str; 6] = [ - "compactDisplay", - "minimumIntegerDigits", - "maximumIntegerDigits", - "minimumFractionDigits", - "maximumFractionDigits", - "roundingMode", -]; - -#[derive(Deserialize, Serialize, Debug)] -#[serde(rename_all = "camelCase")] -struct NumberFormatOptions { - compact_display: Option, - currency_display: Option, - currency_sign: Option, - maximum_fraction_digits: Option, - maximum_integer_digits: Option, - maximum_significant_digits: Option, - minimum_fraction_digits: Option, - minimum_integer_digits: Option, - minimum_significant_digits: Option, - notation: Option, - numbering_system: Option, - rounding_mode: Option, - sign_display: Option, - style: Option, - unit: Option, - unit_display: Option, - use_grouping: Option, - // unsupported options with special labels for conformance - conformance_scale: Option, - conformance_decimal_always: Option, -} - -// Runs decimal and number formatting given patterns or skeletons. -pub fn run_numberformat_test(json_obj: &Value) -> Result { - let label = &json_obj["label"].as_str().unwrap(); - - // Default locale if not specified. - let mut langid: Locale = json_obj - .get("locale") - .map(|locale_name| locale_name.as_str().unwrap().parse().unwrap()) - .unwrap_or_default(); - - let input = &json_obj["input"].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 mut _unsupported_options: Vec<&str> = Vec::new(); - - // If any option is not yet supported, should we report as UNSUPPORTED? - let option_struct: NumberFormatOptions = serde_json::from_str(&options.to_string()).unwrap(); - let mut is_compact = false; - let mut compact_type = ""; - let mut is_scientific = false; - let mut _rounding_mode = ""; - let mut style = ""; - let mut unit = ""; - if option_struct.notation == Some(String::from("compact")) { - is_compact = true; - } - if option_struct.compact_display.is_some() { - compact_type = option_struct.compact_display.as_ref().unwrap(); - } - if option_struct.notation == Some(String::from("scientific")) { - is_scientific = true; - } - if option_struct.style.is_some() { - style = option_struct.style.as_ref().unwrap(); - } - if option_struct.unit.is_some() { - unit = option_struct.unit.as_ref().unwrap(); - } - if option_struct.rounding_mode.is_some() { - _rounding_mode = option_struct.rounding_mode.as_ref().unwrap(); - } - let mut options: options::FixedDecimalFormatterOptions = Default::default(); - // TODO: Use options to call operations including pad and trunc with rounding. - - // !! A test. More options to consider! - match option_struct.use_grouping { - Some(true) => options.grouping_strategy = options::GroupingStrategy::Always, - Some(false) => options.grouping_strategy = options::GroupingStrategy::Never, - _ => options.grouping_strategy = options::GroupingStrategy::Auto, - } - - // -------------------------------------------------------------------------------- - - // UNSUPPORTED THINGS. - // This will change with new additions to ICU4X. - if style == "unit" - || style == "currency" - || unit == "percent" - || is_scientific - || option_struct.minimum_significant_digits.is_some() - || option_struct.maximum_significant_digits.is_some() - || option_struct.conformance_scale.is_some() - || option_struct.conformance_decimal_always.is_some() - || (is_compact - && (option_struct.minimum_fraction_digits.is_some() - || option_struct.maximum_fraction_digits.is_some() - || option_struct.minimum_integer_digits.is_some() - || option_struct.maximum_integer_digits.is_some() - || option_struct.rounding_mode.is_some() - || option_struct.sign_display.is_some())) - { - return Ok(json!({ - "label": label, - "error_detail": {"style": style, - "unit": unit, - "scientific": is_scientific}, - "unsupported": "unit or style not implemented", - "error_type": "unsupported", - })); - } - // -------------------------------------------------------------------------------- - - if let Some(numsys) = option_struct.numbering_system.as_ref() { - langid - .extensions - .unicode - .keywords - .set(key!("nu"), numsys.parse().unwrap()); - } - - // Returns error if parsing the number string fails. - let mut input_num = input.parse::().map_err(|e| e.to_string())?; - - let result_string = if is_compact { - // We saw compact! - let cdf = crate::try_or_return_error!(label, langid, { - if compact_type == "short" { - CompactDecimalFormatter::try_new_short((&langid).into(), Default::default()) - } else { - println!("#{:?}", " LONG"); - CompactDecimalFormatter::try_new_long((&langid).into(), Default::default()) - } - }); - // input.parse().map_err(|e| e.to_string())?; - - let input_num = input.parse::().map_err(|e| e.to_string())?; - let formatted_cdf = cdf.format_fixed_decimal(input_num); - formatted_cdf.write_to_string().into_owned() - // } - // else if is_scientific { - // let mut sci_decimal = input.parse::().map_err(|e| e.to_string()); - // // TEMPORARY - } else { - // FixedDecimal - // Can this fail with invalid options? - let fdf = crate::try_or_return_error!(label, langid, { - FixedDecimalFormatter::try_new((&langid).into(), options.clone()) - }); - - // Apply relevant options for digits. - if let Some(x) = option_struct.maximum_fraction_digits { - let rounding_mode = match option_struct.rounding_mode.as_deref() { - Some("ceil") => RoundingMode::Ceil, - Some("floor") => RoundingMode::Floor, - Some("expand") => RoundingMode::Expand, - Some("trunc") => RoundingMode::Trunc, - Some("halfCeil") => RoundingMode::HalfCeil, - Some("halfFloor") => RoundingMode::HalfFloor, - Some("halfExpand") => RoundingMode::HalfExpand, - Some("halfTrunc") => RoundingMode::HalfTrunc, - Some("halfEven") => RoundingMode::HalfEven, - _ => RoundingMode::HalfEven, - }; - input_num.round_with_mode(-(x as i16), rounding_mode); - input_num.trim_end(); - } - if let Some(x) = option_struct.minimum_fraction_digits { - input_num.pad_end(-(x as i16)); - } - if let Some(x) = option_struct.maximum_integer_digits { - input_num.set_max_position(x as i16); - input_num.trim_start(); - } - if let Some(x) = option_struct.minimum_integer_digits { - input_num.pad_start(x as i16); - } - - let sign_display = match option_struct.sign_display.as_deref() { - Some("auto") => Some(SignDisplay::Auto), - Some("never") => Some(SignDisplay::Never), - Some("always") => Some(SignDisplay::Always), - Some("exceptZero") => Some(SignDisplay::ExceptZero), - Some("negative") => Some(SignDisplay::Negative), - _ => None, - }; - if let Some(sign_display) = sign_display { - input_num.apply_sign_display(sign_display); - } - - // Apply the options and get formatted string. - fdf.format_to_string(&input_num) - }; - - // Result to stdout. - Ok(json!({ - "label": label, - "result": result_string, - "actual_options": format!("{option_struct:?}, {options:?}"), - })) -} diff --git a/executors/rust/common/print_icu4x_version.rs b/executors/rust/common/print_icu4x_version.rs new file mode 100644 index 00000000..9fbfef01 --- /dev/null +++ b/executors/rust/common/print_icu4x_version.rs @@ -0,0 +1,18 @@ +pub fn print(package: &cargo_metadata::Package) { + println!( + "cargo:rustc-env=CONFORMANCE_ICU4X_VERSION={}", + package.version + ); + println!("cargo::rustc-check-cfg=cfg(ver, values(\"1.3\", \"1.4\", \"1.5\", \"2.0-beta1\"))"); + if !package.version.pre.is_empty() { + println!( + "cargo:rustc-cfg=ver=\"{}.{}-{}\"", + package.version.major, package.version.minor, package.version.pre, + ); + } else { + println!( + "cargo:rustc-cfg=ver=\"{}.{}\"", + package.version.major, package.version.minor, + ); + } +} diff --git a/executors/rust/common/print_icu4x_versions_1_3.rs b/executors/rust/common/print_icu4x_versions_1_3.rs index d0c7bc2f..41ef765d 100644 --- a/executors/rust/common/print_icu4x_versions_1_3.rs +++ b/executors/rust/common/print_icu4x_versions_1_3.rs @@ -1,11 +1,13 @@ -pub fn print() { +// Get the version of ICU4X and data + +#[path = "./print_icu4x_version.rs"] +mod print_icu4x_version; + +pub fn main() { let metadata = cargo_metadata::MetadataCommand::new().exec().unwrap(); for package in metadata.packages.iter() { if package.name == "icu" { - println!( - "cargo:rustc-env=CONFORMANCE_ICU4X_VERSION={}", - package.version - ); + print_icu4x_version::print(&package); } } diff --git a/executors/rust/common/print_icu4x_versions_1_5.rs b/executors/rust/common/print_icu4x_versions_1_5.rs index a45840d5..337be5e6 100644 --- a/executors/rust/common/print_icu4x_versions_1_5.rs +++ b/executors/rust/common/print_icu4x_versions_1_5.rs @@ -1,11 +1,13 @@ -pub fn print() { +// Get the version of ICU4X and data + +#[path = "./print_icu4x_version.rs"] +mod print_icu4x_version; + +pub fn main() { let metadata = cargo_metadata::MetadataCommand::new().exec().unwrap(); for package in metadata.packages.iter() { if package.name == "icu" { - println!( - "cargo:rustc-env=CONFORMANCE_ICU4X_VERSION={}", - package.version - ); + print_icu4x_version::print(&package); } else if package.name == "icu_calendar_data" { println!( "cargo:rustc-env=CONFORMANCE_ICU_VERSION={}", diff --git a/executors/rust/common/run_all_tests.rs b/executors/rust/common/run_all_tests.rs index 949b2702..43bc1412 100644 --- a/executors/rust/common/run_all_tests.rs +++ b/executors/rust/common/run_all_tests.rs @@ -20,6 +20,9 @@ use serde_json::{json, Value}; use std::env; use std::io; +#[path = "../src/mod.rs"] +mod executors; + pub type ExecutorFn = fn(&Value) -> Result; pub struct ExecutorFns { @@ -33,8 +36,23 @@ pub struct ExecutorFns { pub run_relativedatetimeformat_test: ExecutorFn, } +pub fn main() -> io::Result<()> { + let executor_fns = ExecutorFns { + run_collation_test: executors::collator::run_collation_test, + run_datetimeformat_test: executors::datetimefmt::run_datetimeformat_test, + run_likelysubtags_test: executors::likelysubtags::run_likelysubtags_test, + run_list_fmt_test: executors::listfmt::run_list_fmt_test, + run_locale_name_test: executors::localenames::run_locale_name_test, + run_numberformat_test: executors::numberfmt::run_numberformat_test, + run_plural_rules_test: executors::pluralrules::run_plural_rules_test, + run_relativedatetimeformat_test: + executors::relativedatetime_fmt::run_relativedatetimeformat_test, + }; + run_all_tests(executor_fns) +} + // Read from stdin, call functions to get json, output the result. -pub fn main(fns: ExecutorFns) -> io::Result<()> { +pub fn run_all_tests(fns: ExecutorFns) -> io::Result<()> { env::set_var("RUST_BACKTRACE", "1"); env_logger::init(); log::info!("Welcome to the ICU4X Conformance Executor"); diff --git a/executors/rust/2.0-beta1/src/collator.rs b/executors/rust/src/collator.rs similarity index 84% rename from executors/rust/2.0-beta1/src/collator.rs rename to executors/rust/src/collator.rs index 33fc0574..28b02663 100644 --- a/executors/rust/2.0-beta1/src/collator.rs +++ b/executors/rust/src/collator.rs @@ -4,7 +4,8 @@ use serde_json::{json, Value}; use core::cmp::Ordering; use icu::collator::*; -use icu::locale::locale; + +use super::compat::{locale, pref}; // Function runs comparison using collator pub fn run_collation_test(json_obj: &Value) -> Result { @@ -14,7 +15,11 @@ pub fn run_collation_test(json_obj: &Value) -> Result { let str1: &str = json_obj["s1"].as_str().unwrap(); let str2: &str = json_obj["s2"].as_str().unwrap(); + #[cfg(any(ver = "1.3", ver = "1.4", ver = "1.5"))] + let mut options = CollatorOptions::new(); + #[cfg(not(any(ver = "1.3", ver = "1.4", ver = "1.5")))] let mut options = CollatorOptions::default(); + options.strength = Some(Strength::Tertiary); // Ignore punctuation only if using shifted test. @@ -24,7 +29,7 @@ pub fn run_collation_test(json_obj: &Value) -> Result { } } - let collator = Collator::try_new(locale!("en").into(), options).unwrap(); + let collator = Collator::try_new(pref!(locale!("en")), options).unwrap(); let comparison = collator.compare(str1, str2); diff --git a/executors/rust/src/compat.rs b/executors/rust/src/compat.rs new file mode 100644 index 00000000..76ced040 --- /dev/null +++ b/executors/rust/src/compat.rs @@ -0,0 +1,23 @@ +#![allow(unused_imports)] // not all versions use all imports + +#[cfg(any(ver = "1.3", ver = "1.4", ver = "1.5"))] +pub use icu::locid::{extensions::unicode, locale, LanguageIdentifier, Locale}; + +#[cfg(not(any(ver = "1.3", ver = "1.4", ver = "1.5")))] +pub use icu::locale::{extensions::unicode, locale, LanguageIdentifier, Locale}; + +#[cfg(any(ver = "1.3", ver = "1.4", ver = "1.5"))] +macro_rules! pref { + ($arg:expr) => { + &($arg).into() + }; +} + +#[cfg(not(any(ver = "1.3", ver = "1.4", ver = "1.5")))] +macro_rules! pref { + ($arg:expr) => { + ($arg).into() + }; +} + +pub(crate) use pref; diff --git a/executors/rust/1.3/src/datetimefmt.rs b/executors/rust/src/datetime_1.rs similarity index 98% rename from executors/rust/1.3/src/datetimefmt.rs rename to executors/rust/src/datetime_1.rs index 7e195772..e30b857d 100644 --- a/executors/rust/1.3/src/datetimefmt.rs +++ b/executors/rust/src/datetime_1.rs @@ -2,6 +2,9 @@ // https://docs.rs/icu/1.3.2/icu/datetime/input/trait.TimeZoneInput.html // https://docs.rs/ixdtf/latest/ixdtf/ +// IanaToBcp47Mapper deprecated in 1.5 +#![cfg_attr(ver = "1.5", allow(deprecated))] + use icu::calendar::DateTime; use icu::datetime::{ options::components, options::length, options::DateTimeFormatterOptions, pattern::reference, diff --git a/executors/rust/2.0-beta1/src/datetimefmt.rs b/executors/rust/src/datetime_2.rs similarity index 99% rename from executors/rust/2.0-beta1/src/datetimefmt.rs rename to executors/rust/src/datetime_2.rs index 6fad6857..b45e6e44 100644 --- a/executors/rust/2.0-beta1/src/datetimefmt.rs +++ b/executors/rust/src/datetime_2.rs @@ -124,7 +124,7 @@ pub fn run_datetimeformat_test(json_obj: &Value) -> Result { let input_iso = &json_obj["original_input"].as_str().unwrap(); // Extract all the information we need from the string - let input_zoned_date_time = crate::try_or_return_error!(label, locale, { + let input_zoned_date_time = super::try_or_return_error!(label, locale, { IxdtfParser::new() .try_from_str(&input_iso) .map_err(|e| format!("{e:?}")) diff --git a/executors/rust/2.0-beta1/src/decimalfmt.rs b/executors/rust/src/decimalfmt.rs similarity index 92% rename from executors/rust/2.0-beta1/src/decimalfmt.rs rename to executors/rust/src/decimalfmt.rs index 99625498..fa5dcc43 100644 --- a/executors/rust/2.0-beta1/src/decimalfmt.rs +++ b/executors/rust/src/decimalfmt.rs @@ -5,7 +5,7 @@ use serde_json::{json, Value}; use icu::decimal::options; use icu::decimal::FixedDecimalFormatter; -use icu::locale::Locale; +use super::compat::{pref, Locale}; // Runs decimal and number formatting given patterns or skeletons. pub fn _todo(json_obj: &Value) -> Result { @@ -24,7 +24,7 @@ pub fn _todo(json_obj: &Value) -> Result { // !! A test. More options to consider! options.grouping_strategy = options::GroupingStrategy::Min2; - let fdf = FixedDecimalFormatter::try_new(langid.into(), options) + let fdf = FixedDecimalFormatter::try_new(pref!(langid), options) .expect("Data should load successfully"); // Check if the conversion from the string input is OK. diff --git a/executors/rust/2.0-beta1/src/displaynames.rs b/executors/rust/src/displaynames.rs similarity index 78% rename from executors/rust/2.0-beta1/src/displaynames.rs rename to executors/rust/src/displaynames.rs index f5ee0509..30479685 100644 --- a/executors/rust/2.0-beta1/src/displaynames.rs +++ b/executors/rust/src/displaynames.rs @@ -2,8 +2,13 @@ use serde_json::{json, Value}; +#[cfg(any(ver = "1.3", ver = "1.4"))] +use icu::displaynames::*; + +#[cfg(any(ver = "1.5", ver = "2.0-beta1"))] use icu::experimental::displaynames::*; -use icu::locale::Locale; + +use super::compat::{pref, Locale}; // Function runs comparison using displaynames pub fn _todo(json_obj: &Value) -> Result { @@ -18,7 +23,7 @@ pub fn _todo(json_obj: &Value) -> Result { .unwrap_or_default(); let displaynames = - LocaleDisplayNamesFormatter::try_new(langid.into(), Default::default()).unwrap(); + LocaleDisplayNamesFormatter::try_new(pref!(langid), Default::default()).unwrap(); Ok(json!({ "label": label, diff --git a/executors/rust/2.0-beta1/src/likelysubtags.rs b/executors/rust/src/likelysubtags.rs similarity index 55% rename from executors/rust/2.0-beta1/src/likelysubtags.rs rename to executors/rust/src/likelysubtags.rs index 69a5b94e..0732b46b 100644 --- a/executors/rust/2.0-beta1/src/likelysubtags.rs +++ b/executors/rust/src/likelysubtags.rs @@ -2,7 +2,17 @@ use serde_json::{json, Value}; -use icu::locale::{LanguageIdentifier, LocaleExpander}; +#[cfg(any(ver = "1.3", ver = "1.4", ver = "1.5"))] +pub use icu::locid_transform::LocaleExpander; + +#[cfg(not(any(ver = "1.3", ver = "1.4", ver = "1.5")))] +pub use icu::locale::LocaleExpander; + +#[cfg(any(ver = "1.3", ver = "1.4", ver = "1.5"))] +type LocaleType = super::compat::Locale; + +#[cfg(not(any(ver = "1.3", ver = "1.4", ver = "1.5")))] +type LocaleType = super::compat::LanguageIdentifier; // https://docs.rs/icu/latest/icu/locid_transform/ @@ -16,10 +26,23 @@ pub fn run_likelysubtags_test(json_obj: &Value) -> Result { let locale_str: &str = json_obj["locale"].as_str().unwrap(); - let mut locale = locale_str.parse::().unwrap(); + let mut locale = locale_str.parse::().unwrap(); if test_option == &"minimizeFavorScript" { - lc.minimize_favor_script(&mut locale); + #[cfg(any(ver = "1.3", ver = "1.4"))] + { + // This option is not yet supported. + return Ok(json!({ + "label": label, + "error_detail": {"option": test_option}, + "unsupported": test_option, + "error_type": "unsupported", + })); + } + #[cfg(not(any(ver = "1.3", ver = "1.4")))] + { + lc.minimize_favor_script(&mut locale); + } } else if test_option == &"minimize" || test_option == &"minimizeFavorRegion" { lc.minimize(&mut locale); } else if test_option == &"maximize" { @@ -34,12 +57,6 @@ pub fn run_likelysubtags_test(json_obj: &Value) -> Result { })); } - if test_option == &"minimize" { - lc.minimize(&mut locale); - } else if test_option == &"maximize" { - lc.maximize(&mut locale); - } - let json_result = json!({ "label": label, "result": locale.to_string() diff --git a/executors/rust/2.0-beta1/src/listfmt.rs b/executors/rust/src/listfmt.rs similarity index 75% rename from executors/rust/2.0-beta1/src/listfmt.rs rename to executors/rust/src/listfmt.rs index cd0a2c4c..deac8116 100644 --- a/executors/rust/2.0-beta1/src/listfmt.rs +++ b/executors/rust/src/listfmt.rs @@ -4,8 +4,8 @@ use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; use icu::list::*; -use icu::locale::Locale; -use icu_provider::DataError; + +use super::compat::{pref, Locale}; use writeable::Writeable; @@ -48,23 +48,40 @@ pub fn run_list_fmt_test(json_obj: &Value) -> Result { })); }; - let list_formatter_result: Result = if list_type == "conjunction" { - // Reference: https://docs.rs/icu/latest/icu/list/index.html - ListFormatter::try_new_and( - locale.into(), + let prefs = pref!(locale); + + #[cfg(any(ver = "1.3", ver = "1.4", ver = "1.5"))] + let formatter_option = if list_type == "conjunction" { + Some(ListFormatter::try_new_and_with_length(prefs, list_style)) + } else if list_type == "disjunction" { + Some(ListFormatter::try_new_or_with_length(prefs, list_style)) + } else if list_type == "unit" { + Some(ListFormatter::try_new_unit_with_length(prefs, list_style)) + } else { + None + }; + + #[cfg(not(any(ver = "1.3", ver = "1.4", ver = "1.5")))] + let formatter_option = if list_type == "conjunction" { + Some(ListFormatter::try_new_and( + prefs, ListFormatterOptions::default().with_length(list_style), - ) + )) } else if list_type == "disjunction" { - ListFormatter::try_new_or( - locale.into(), + Some(ListFormatter::try_new_or( + prefs, ListFormatterOptions::default().with_length(list_style), - ) + )) } else if list_type == "unit" { - ListFormatter::try_new_unit( - locale.into(), + Some(ListFormatter::try_new_unit( + prefs, ListFormatterOptions::default().with_length(list_style), - ) + )) } else { + None + }; + + let Some(list_formatter_result) = formatter_option else { // This option is not supported. return Ok(json!({ "label": label, diff --git a/executors/rust/1.3/src/localenames.rs b/executors/rust/src/localenames.rs similarity index 94% rename from executors/rust/1.3/src/localenames.rs rename to executors/rust/src/localenames.rs index 8ca2d343..ad3ecf23 100644 --- a/executors/rust/1.3/src/localenames.rs +++ b/executors/rust/src/localenames.rs @@ -2,11 +2,13 @@ use serde_json::{json, Value}; -use crate::icu::displaynames::{DisplayNamesOptions, LocaleDisplayNamesFormatter}; +use super::compat::{pref, Locale}; -use icu::locid::Locale; +#[cfg(any(ver = "1.3", ver = "1.4"))] +use icu::displaynames::*; -use crate::icu::displaynames::LanguageDisplay; +#[cfg(any(ver = "1.5", ver = "2.0-beta1"))] +use icu::experimental::displaynames::*; // Function runs locale names tests pub fn run_locale_name_test(json_obj: &Value) -> Result { @@ -81,7 +83,7 @@ pub fn run_locale_name_test(json_obj: &Value) -> Result { } }; - let display_name_formatter = LocaleDisplayNamesFormatter::try_new(&langid.into(), options); + let display_name_formatter = LocaleDisplayNamesFormatter::try_new(pref!(langid), options); let json_result = match display_name_formatter { Ok(formatter) => { diff --git a/executors/rust/src/mod.rs b/executors/rust/src/mod.rs new file mode 100644 index 00000000..13d73952 --- /dev/null +++ b/executors/rust/src/mod.rs @@ -0,0 +1,22 @@ +mod compat; +mod try_or_return_error; + +pub mod collator; +pub mod decimalfmt; +pub mod displaynames; +pub mod likelysubtags; +pub mod listfmt; +pub mod localenames; +pub mod numberfmt; +pub mod pluralrules; +pub mod relativedatetime_fmt; + +#[cfg(any(ver = "1.3", ver = "1.4", ver = "1.5"))] +#[path = "datetime_1.rs"] +pub mod datetimefmt; + +#[cfg(any(ver = "2.0-beta1"))] +#[path = "datetime_2.rs"] +pub mod datetimefmt; + +use try_or_return_error::try_or_return_error; diff --git a/executors/rust/1.3/src/numberfmt.rs b/executors/rust/src/numberfmt.rs similarity index 80% rename from executors/rust/1.3/src/numberfmt.rs rename to executors/rust/src/numberfmt.rs index 75790c94..ac69f8df 100644 --- a/executors/rust/1.3/src/numberfmt.rs +++ b/executors/rust/src/numberfmt.rs @@ -4,18 +4,21 @@ use fixed_decimal::FixedDecimal; use fixed_decimal::SignDisplay; // TODO: use fixed_decimal::ScientificDecimal; +use super::compat::{pref, unicode, Locale}; use icu::decimal::options; use icu::decimal::FixedDecimalFormatter; -use crate::icu::compactdecimal::CompactDecimalFormatter; - -use icu::locid::{extensions::unicode::key, Locale}; - use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; use writeable::Writeable; +#[cfg(any(ver = "1.3", ver = "1.4"))] +use icu::compactdecimal::CompactDecimalFormatter; + +#[cfg(any(ver = "1.5", ver = "2.0-beta1"))] +use icu::experimental::compactdecimal::CompactDecimalFormatter; + // Support options - update when ICU4X adds support static _SUPPORTED_OPTIONS: [&str; 6] = [ "compactDisplay", @@ -141,7 +144,7 @@ pub fn run_numberformat_test(json_obj: &Value) -> Result { .extensions .unicode .keywords - .set(key!("nu"), numsys.parse().unwrap()); + .set(unicode::key!("nu"), numsys.parse().unwrap()); } // Returns error if parsing the number string fails. @@ -149,12 +152,14 @@ pub fn run_numberformat_test(json_obj: &Value) -> Result { let result_string = if is_compact { // We saw compact! - let cdf = if compact_type == "short" { - CompactDecimalFormatter::try_new_short(&langid.into(), Default::default()).unwrap() - } else { - println!("#{:?}", " LONG"); - CompactDecimalFormatter::try_new_long(&langid.into(), Default::default()).unwrap() - }; + let cdf = super::try_or_return_error!(label, langid, { + if compact_type == "short" { + CompactDecimalFormatter::try_new_short(pref!(&langid), Default::default()) + } else { + println!("#{:?}", " LONG"); + CompactDecimalFormatter::try_new_long(pref!(&langid), Default::default()) + } + }); // input.parse().map_err(|e| e.to_string())?; let input_num = input.parse::().map_err(|e| e.to_string())?; @@ -166,12 +171,13 @@ pub fn run_numberformat_test(json_obj: &Value) -> Result { // // TEMPORARY } else { // FixedDecimal - // Can this fail with invalid options? - let fdf = FixedDecimalFormatter::try_new(&langid.into(), options.clone()) - .expect("Data should load successfully"); + let fdf = super::try_or_return_error!(label, langid, { + FixedDecimalFormatter::try_new(pref!(&langid), options.clone()) + }); // Apply relevant options for digits. if let Some(x) = option_struct.maximum_fraction_digits { + #[cfg(any(ver = "1.3", ver = "1.4", ver = "1.5"))] match option_struct.rounding_mode.as_deref() { Some("ceil") => input_num.ceil(-(x as i16)), Some("floor") => input_num.floor(-(x as i16)), @@ -184,6 +190,22 @@ pub fn run_numberformat_test(json_obj: &Value) -> Result { Some("halfEven") => input_num.half_even(-(x as i16)), _ => input_num.half_even(-(x as i16)), }; + #[cfg(not(any(ver = "1.3", ver = "1.4", ver = "1.5")))] + input_num.round_with_mode( + -(x as i16), + match option_struct.rounding_mode.as_deref() { + Some("ceil") => fixed_decimal::RoundingMode::Ceil, + Some("floor") => fixed_decimal::RoundingMode::Floor, + Some("expand") => fixed_decimal::RoundingMode::Expand, + Some("trunc") => fixed_decimal::RoundingMode::Trunc, + Some("halfCeil") => fixed_decimal::RoundingMode::HalfCeil, + Some("halfFloor") => fixed_decimal::RoundingMode::HalfFloor, + Some("halfExpand") => fixed_decimal::RoundingMode::HalfExpand, + Some("halfTrunc") => fixed_decimal::RoundingMode::HalfTrunc, + Some("halfEven") => fixed_decimal::RoundingMode::HalfEven, + _ => fixed_decimal::RoundingMode::HalfEven, + }, + ); input_num.trim_end(); } if let Some(x) = option_struct.minimum_fraction_digits { diff --git a/executors/rust/2.0-beta1/src/pluralrules.rs b/executors/rust/src/pluralrules.rs similarity index 96% rename from executors/rust/2.0-beta1/src/pluralrules.rs rename to executors/rust/src/pluralrules.rs index bf3c5ce5..125effb8 100644 --- a/executors/rust/2.0-beta1/src/pluralrules.rs +++ b/executors/rust/src/pluralrules.rs @@ -1,8 +1,9 @@ use fixed_decimal::FixedDecimal; -use icu::locale::Locale; use serde_json::{json, Value}; use std::str::FromStr; +use super::compat::{pref, Locale}; + // https://docs.rs/icu/latest/icu/plurals/index.html use icu::plurals::{PluralCategory, PluralRuleType, PluralRules}; @@ -49,7 +50,7 @@ pub fn run_plural_rules_test(json_obj: &Value) -> Result { }; let pr = - PluralRules::try_new(locale.into(), plural_type.into()).expect("locale should be present"); + PluralRules::try_new(pref!(locale), plural_type.into()).expect("locale should be present"); // Get the category and convert to a string. let category = pr.category_for(&test_number); diff --git a/executors/rust/2.0-beta1/src/relativedatetime_fmt.rs b/executors/rust/src/relativedatetime_fmt.rs similarity index 67% rename from executors/rust/2.0-beta1/src/relativedatetime_fmt.rs rename to executors/rust/src/relativedatetime_fmt.rs index 0f61b541..43582a05 100644 --- a/executors/rust/2.0-beta1/src/relativedatetime_fmt.rs +++ b/executors/rust/src/relativedatetime_fmt.rs @@ -3,15 +3,21 @@ use fixed_decimal::FixedDecimal; -use icu::locale::extensions::unicode; -use icu::locale::extensions::unicode::key; -use icu::locale::Locale; -use icu_provider::DataError; +use super::compat::{pref, unicode, unicode::key, Locale}; use std::str::FromStr; -use icu::experimental::relativetime::options::Numeric; -use icu::experimental::relativetime::{RelativeTimeFormatter, RelativeTimeFormatterOptions}; +#[cfg(any(ver = "1.3", ver = "1.4"))] +use icu::relativetime::{options::Numeric, *}; + +#[cfg(any(ver = "1.5", ver = "2.0-beta1"))] +use icu::experimental::relativetime::{options::Numeric, *}; + +#[cfg(any(ver = "1.3", ver = "1.4", ver = "1.5"))] +type Error = RelativeTimeError; + +#[cfg(not(any(ver = "1.3", ver = "1.4", ver = "1.5")))] +type Error = icu_provider::DataError; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; @@ -29,99 +35,99 @@ fn get_formatter_from_unit_style( unit: String, style: String, options: RelativeTimeFormatterOptions, -) -> Result { - let locale = locale.into(); +) -> Result { + let prefs = pref!(locale); if unit == "year" { if style == "long" { - RelativeTimeFormatter::try_new_long_year(locale, options) + RelativeTimeFormatter::try_new_long_year(prefs, options) } else if style == "short" { - RelativeTimeFormatter::try_new_short_year(locale, options) + RelativeTimeFormatter::try_new_short_year(prefs, options) } else if style == "narrow" { - RelativeTimeFormatter::try_new_narrow_year(locale, options) + RelativeTimeFormatter::try_new_narrow_year(prefs, options) } else { // Assume long - RelativeTimeFormatter::try_new_long_year(locale, options) + RelativeTimeFormatter::try_new_long_year(prefs, options) } } else if unit == "quarter" { if style == "long" { - RelativeTimeFormatter::try_new_long_quarter(locale, options) + RelativeTimeFormatter::try_new_long_quarter(prefs, options) } else if style == "short" { - RelativeTimeFormatter::try_new_short_quarter(locale, options) + RelativeTimeFormatter::try_new_short_quarter(prefs, options) } else if style == "narrow" { - RelativeTimeFormatter::try_new_narrow_quarter(locale, options) + RelativeTimeFormatter::try_new_narrow_quarter(prefs, options) } else { // Assume long - RelativeTimeFormatter::try_new_long_quarter(locale, options) + RelativeTimeFormatter::try_new_long_quarter(prefs, options) } } else if unit == "month" { if style == "long" { - RelativeTimeFormatter::try_new_long_month(locale, options) + RelativeTimeFormatter::try_new_long_month(prefs, options) } else if style == "short" { - RelativeTimeFormatter::try_new_short_month(locale, options) + RelativeTimeFormatter::try_new_short_month(prefs, options) } else if style == "narrow" { - RelativeTimeFormatter::try_new_narrow_month(locale, options) + RelativeTimeFormatter::try_new_narrow_month(prefs, options) } else { // Assume long - RelativeTimeFormatter::try_new_long_month(locale, options) + RelativeTimeFormatter::try_new_long_month(prefs, options) } } else if unit == "week" { if style == "long" { - RelativeTimeFormatter::try_new_long_week(locale, options) + RelativeTimeFormatter::try_new_long_week(prefs, options) } else if style == "short" { - RelativeTimeFormatter::try_new_short_week(locale, options) + RelativeTimeFormatter::try_new_short_week(prefs, options) } else if style == "narrow" { - RelativeTimeFormatter::try_new_narrow_week(locale, options) + RelativeTimeFormatter::try_new_narrow_week(prefs, options) } else { // Assume long - RelativeTimeFormatter::try_new_long_week(locale, options) + RelativeTimeFormatter::try_new_long_week(prefs, options) } } else if unit == "day" { if style == "long" { - RelativeTimeFormatter::try_new_long_day(locale, options) + RelativeTimeFormatter::try_new_long_day(prefs, options) } else if style == "short" { - RelativeTimeFormatter::try_new_short_day(locale, options) + RelativeTimeFormatter::try_new_short_day(prefs, options) } else if style == "narrow" { - RelativeTimeFormatter::try_new_narrow_day(locale, options) + RelativeTimeFormatter::try_new_narrow_day(prefs, options) } else { // Assume long - RelativeTimeFormatter::try_new_long_day(locale, options) + RelativeTimeFormatter::try_new_long_day(prefs, options) } } else if unit == "hour" { if style == "long" { - RelativeTimeFormatter::try_new_long_hour(locale, options) + RelativeTimeFormatter::try_new_long_hour(prefs, options) } else if style == "short" { - RelativeTimeFormatter::try_new_short_hour(locale, options) + RelativeTimeFormatter::try_new_short_hour(prefs, options) } else if style == "narrow" { - RelativeTimeFormatter::try_new_narrow_hour(locale, options) + RelativeTimeFormatter::try_new_narrow_hour(prefs, options) } else { // Assume long - RelativeTimeFormatter::try_new_long_hour(locale, options) + RelativeTimeFormatter::try_new_long_hour(prefs, options) } } else if unit == "minute" { if style == "long" { - RelativeTimeFormatter::try_new_long_minute(locale, options) + RelativeTimeFormatter::try_new_long_minute(prefs, options) } else if style == "short" { - RelativeTimeFormatter::try_new_short_minute(locale, options) + RelativeTimeFormatter::try_new_short_minute(prefs, options) } else if style == "narrow" { - RelativeTimeFormatter::try_new_narrow_minute(locale, options) + RelativeTimeFormatter::try_new_narrow_minute(prefs, options) } else { // Assume long - RelativeTimeFormatter::try_new_long_minute(locale, options) + RelativeTimeFormatter::try_new_long_minute(prefs, options) } } else if unit == "second" { if style == "long" { - RelativeTimeFormatter::try_new_long_second(locale, options) + RelativeTimeFormatter::try_new_long_second(prefs, options) } else if style == "short" { - RelativeTimeFormatter::try_new_short_second(locale, options) + RelativeTimeFormatter::try_new_short_second(prefs, options) } else if style == "narrow" { - RelativeTimeFormatter::try_new_narrow_second(locale, options) + RelativeTimeFormatter::try_new_narrow_second(prefs, options) } else { // Assume long - RelativeTimeFormatter::try_new_long_second(locale, options) + RelativeTimeFormatter::try_new_long_second(prefs, options) } } else { // An unknown unit! - RelativeTimeFormatter::try_new_narrow_second(locale, options) + RelativeTimeFormatter::try_new_narrow_second(prefs, options) } } @@ -169,8 +175,7 @@ pub fn run_relativedatetimeformat_test(json_obj: &Value) -> Result Result {{ match ($expr) { @@ -14,3 +13,5 @@ macro_rules! try_or_return_error { } }}; } + +pub(crate) use try_or_return_error;