From 75a09f3ff666981517fbf8ebeeb4060e2cd2d625 Mon Sep 17 00:00:00 2001 From: Clyde Gerber Date: Wed, 25 Oct 2023 13:49:23 -0400 Subject: [PATCH] Set the resolver version to 2, clarify the error message when the ICU version isn't found and add implementations for ICU functions: - uloc_getName(), - uloc_getISO3Language(), - uloc_getISO3Country(), - uloc_getDisplayLanguage(), - uloc_getDisplayScript(), - uloc_getDisplayCountry(), - uloc_getDisplayVariant(), - uloc_getDisplayKeyword(), - uloc_getDisplayKeywordValue(), and - uloc_getDisplayName()). --- Cargo.toml | 1 + rust_icu_sys/build.rs | 2 +- rust_icu_udat/build.rs | 2 +- rust_icu_uloc/src/lib.rs | 254 +++++++++++++++++++++++++++++++++++- rust_icu_ustring/src/lib.rs | 2 +- 5 files changed, 257 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0396387c..ecf93269 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,3 +26,4 @@ members = [ "rust_icu_utext", "rust_icu_utrans", ] +resolver = "2" diff --git a/rust_icu_sys/build.rs b/rust_icu_sys/build.rs index 2922387b..b2794572 100644 --- a/rust_icu_sys/build.rs +++ b/rust_icu_sys/build.rs @@ -225,7 +225,7 @@ mod inner { Ok(str) } }) - .with_context(|| "failed to get ICU version; please ensure icu-config in $PATH") + .with_context(|| "failed to get ICU version; please ensure pkg-config in $PATH") } fn install_dir(&mut self) -> Result { diff --git a/rust_icu_udat/build.rs b/rust_icu_udat/build.rs index aff9faf2..a1206ec1 100644 --- a/rust_icu_udat/build.rs +++ b/rust_icu_udat/build.rs @@ -76,7 +76,7 @@ mod inner { pub fn version(&mut self) -> Result { self.rep .run(&["--modversion", "icu-i18n"]) - .with_context(|| "while getting ICU version; is icu-config in $PATH?") + .with_context(|| "while getting ICU version; is pkg-config in $PATH?") } /// Returns the config major number. For example, will return "64" for diff --git a/rust_icu_uloc/src/lib.rs b/rust_icu_uloc/src/lib.rs index 647619ff..ab37a896 100644 --- a/rust_icu_uloc/src/lib.rs +++ b/rust_icu_uloc/src/lib.rs @@ -15,13 +15,16 @@ use { rust_icu_common as common, rust_icu_common::buffered_string_method_with_retry, + rust_icu_sys as sys, rust_icu_sys::versioned_function, rust_icu_sys::*, rust_icu_uenum::Enumeration, + rust_icu_ustring as ustring, + rust_icu_ustring::buffered_uchar_method_with_retry, std::{ cmp::Ordering, collections::HashMap, - convert::{From, TryFrom}, + convert::{From, TryFrom, TryInto}, ffi, fmt, os::raw, }, @@ -209,12 +212,155 @@ impl ULoc { self.call_buffered_string_method_to_option(versioned_function!(uloc_getVariant)) } + /// Implements `uloc_getName`. + pub fn name(&self) -> Option { + self.call_buffered_string_method_to_option(versioned_function!(uloc_getName)) + } + /// Implements `uloc_canonicalize` from ICU4C. pub fn canonicalize(&self) -> Result { self.call_buffered_string_method(versioned_function!(uloc_canonicalize)) .map(|repr| ULoc { repr }) } + /// Implements 'uloc_getISO3Language' from ICU4C. + pub fn iso3_language(&self) -> Option { + let lang = unsafe { + ffi::CStr::from_ptr( + versioned_function!(uloc_getISO3Language)(self.as_c_str().as_ptr()) + ).to_str() + }; + let value = lang.unwrap(); + if value.is_empty() { + None + } else { + Some(value.to_string()) + } + } + + /// Implements 'uloc_getISO3Country' from ICU4C. + pub fn iso3_country(&self) -> Option { + let country = unsafe { + ffi::CStr::from_ptr( + versioned_function!(uloc_getISO3Country)(self.as_c_str().as_ptr()) + ).to_str() + }; + let value = country.unwrap(); + if value.is_empty() { + None + } else { + Some(value.to_string()) + } + } + + /// Implements `uloc_getDisplayLanguage`. + pub fn display_language(&self, display_locale: &ULoc) -> Result { + buffered_uchar_method_with_retry!( + display_language_impl, + LOCALE_CAPACITY, + [locale: *const raw::c_char, display_locale: *const raw::c_char,], + [] + ); + display_language_impl( + versioned_function!(uloc_getDisplayLanguage), + self.as_c_str().as_ptr(), + display_locale.as_c_str().as_ptr(), + ) + } + + /// Implements `uloc_getDisplayScript`. + pub fn display_script(&self, display_locale: &ULoc) -> Result { + buffered_uchar_method_with_retry!( + display_script_impl, + LOCALE_CAPACITY, + [locale: *const raw::c_char, display_locale: *const raw::c_char,], + [] + ); + display_script_impl( + versioned_function!(uloc_getDisplayScript), + self.as_c_str().as_ptr(), + display_locale.as_c_str().as_ptr(), + ) + } + + /// Implements `uloc_getDisplayCountry`. + pub fn display_country(&self, display_locale: &ULoc) -> Result { + buffered_uchar_method_with_retry!( + display_country_impl, + LOCALE_CAPACITY, + [locale: *const raw::c_char, display_locale: *const raw::c_char,], + [] + ); + display_country_impl( + versioned_function!(uloc_getDisplayCountry), + self.as_c_str().as_ptr(), + display_locale.as_c_str().as_ptr(), + ) + } + + /// Implements `uloc_getDisplayVariant`. + pub fn display_variant(&self, display_locale: &ULoc) -> Result { + buffered_uchar_method_with_retry!( + display_variant_impl, + LOCALE_CAPACITY, + [locale: *const raw::c_char, display_locale: *const raw::c_char,], + [] + ); + display_variant_impl( + versioned_function!(uloc_getDisplayCountry), + self.as_c_str().as_ptr(), + display_locale.as_c_str().as_ptr(), + ) + } + + /// Implements `uloc_getDisplayKeyword`. + pub fn display_keyword(keyword: &String, display_locale: &ULoc) -> Result { + buffered_uchar_method_with_retry!( + display_keyword_impl, + LOCALE_CAPACITY, + [keyword: *const raw::c_char, display_locale: *const raw::c_char,], + [] + ); + let keyword = ffi::CString::new(keyword.as_str()).unwrap(); + display_keyword_impl( + versioned_function!(uloc_getDisplayKeyword), + keyword.as_ptr(), + display_locale.as_c_str().as_ptr(), + ) + } + + /// Implements `uloc_getDisplayKeywordValue`. + pub fn display_keyword_value(&self, keyword: &String, display_locale: &ULoc) -> Result { + buffered_uchar_method_with_retry!( + display_keyword_value_impl, + LOCALE_CAPACITY, + [keyword: *const raw::c_char, value: *const raw::c_char, display_locale: *const raw::c_char,], + [] + ); + let keyword = ffi::CString::new(keyword.as_str()).unwrap(); + display_keyword_value_impl( + versioned_function!(uloc_getDisplayKeywordValue), + self.as_c_str().as_ptr(), + keyword.as_ptr(), + display_locale.as_c_str().as_ptr(), + ) + } + + /// Implements `uloc_getDisplayName`. + pub fn display_name(&self, display_locale: &ULoc) -> Result { + buffered_uchar_method_with_retry!( + display_name_impl, + LOCALE_CAPACITY, + [locale: *const raw::c_char, display_locale: *const raw::c_char,], + [] + ); + display_name_impl( + versioned_function!(uloc_getDisplayName), + self.as_c_str().as_ptr(), + display_locale.as_c_str().as_ptr(), + ) + } + /// Implements `uloc_addLikelySubtags` from ICU4C. pub fn add_likely_subtags(&self) -> Result { self.call_buffered_string_method(versioned_function!(uloc_addLikelySubtags)) @@ -634,6 +780,20 @@ mod tests { Ok(()) } + #[test] + fn test_name() -> Result<(), Error> { + let loc = ULoc::try_from("en-US")?; + match loc.name() { + None => assert!(false), + Some(name) => assert_eq!(name, "en_US"), + } + let loc = ULoc::try_from("und")?; + assert_eq!(loc.name(), None); + let loc = ULoc::try_from("")?; + assert_eq!(loc.name(), None); + Ok(()) + } + #[test] fn test_default_locale() { let loc = ULoc::try_from("fr-fr").expect("get fr_FR locale"); @@ -985,4 +1145,96 @@ mod tests { let loc = ULoc::try_from("sr@timezone=America/Los_Angeles").unwrap(); assert_eq!(ULoc::for_language_tag("sr-u-tz-uslax").unwrap(), loc); } + + #[test] + fn test_for_language_error() { + let loc = ULoc::for_language_tag("en_US").unwrap(); + assert_eq!(loc.language(), None); + assert_eq!(loc.country(), None); + } + + #[test] + fn test_iso3_language() { + let loc = ULoc::for_language_tag("en-US").unwrap(); + let iso_lang = loc.iso3_language(); + assert_eq!(iso_lang, Some("eng".to_string())); + let loc = ULoc::for_language_tag("und").unwrap(); + let iso_lang = loc.iso3_language(); + assert_eq!(iso_lang, None); + } + + #[test] + fn test_iso3_country() { + let loc = ULoc::for_language_tag("en-US").unwrap(); + let iso_country = loc.iso3_country(); + assert_eq!(iso_country, Some("USA".to_string())); + let loc = ULoc::for_language_tag("und").unwrap(); + let iso_country = loc.iso3_country(); + assert_eq!(iso_country, None); + } + + #[test] + fn test_display_language() { + let english_locale = ULoc::for_language_tag("en").unwrap(); + let french_locale = ULoc::for_language_tag("fr").unwrap(); + let english_in_french = english_locale.display_language(&french_locale); + assert!(english_in_french.is_ok()); + assert_eq!(english_in_french.unwrap().as_string_debug(), "anglais"); + let root_locale = ULoc::for_language_tag("und").unwrap(); + assert_eq!(root_locale.display_language(&french_locale).unwrap().as_string_debug(), "langue indéterminée"); + let root_locale = ULoc::for_language_tag("en_US").unwrap(); + assert_eq!(root_locale.display_language(&french_locale).unwrap().as_string_debug(), "langue indéterminée"); + } + + #[test] + fn test_display_script() { + let english_latin_locale = ULoc::for_language_tag("en-latg").unwrap(); + let french_locale = ULoc::for_language_tag("fr").unwrap(); + let latin_script_in_french = english_latin_locale.display_script(&french_locale); + assert!(latin_script_in_french.is_ok()); + assert_eq!(latin_script_in_french.unwrap().as_string_debug(), "latin (variante gaélique)"); + let english_locale = ULoc::for_language_tag("en").unwrap(); + assert_eq!(english_locale.display_script(&french_locale).unwrap().as_string_debug(), ""); + } + + #[test] + fn test_display_country() { + let usa_locale = ULoc::for_language_tag("en-US").unwrap(); + let french_locale = ULoc::for_language_tag("fr").unwrap(); + let usa_in_french = usa_locale.display_country(&french_locale); + assert!(usa_in_french.is_ok()); + assert_eq!(usa_in_french.unwrap().as_string_debug(), "États-Unis"); + let english_locale = ULoc::for_language_tag("en").unwrap(); + assert_eq!(english_locale.display_script(&french_locale).unwrap().as_string_debug(), ""); + } + + #[test] + fn test_display_keyword() { + let french_locale = ULoc::for_language_tag("fr").unwrap(); + let currency_in_french = ULoc::display_keyword(&"currency".to_string(), &french_locale); + assert!(currency_in_french.is_ok()); + assert_eq!(currency_in_french.unwrap().as_string_debug(), "devise"); + } + + #[test] + fn test_display_keyword_value() { + let locale_w_calendar = ULoc::for_language_tag("az-Cyrl-AZ-u-ca-hebrew-t-it-x-whatever").unwrap(); + let french_locale = ULoc::for_language_tag("fr").unwrap(); + let calendar_value_in_french = locale_w_calendar.display_keyword_value( + &"calendar".to_string(), &french_locale); + assert!(calendar_value_in_french.is_ok()); + assert_eq!(calendar_value_in_french.unwrap().as_string_debug(), "calendrier hébraïque"); + } + + #[test] + fn test_display_name() { + let loc = ULoc::for_language_tag("az-Cyrl-AZ-u-ca-hebrew-t-it-x-whatever").unwrap(); + let french_locale = ULoc::for_language_tag("fr").unwrap(); + let display_name_in_french = loc.display_name(&french_locale); + assert!(display_name_in_french.is_ok()); + assert_eq!( + display_name_in_french.unwrap().as_string_debug(), + "azerbaïdjanais (cyrillique, Azerbaïdjan, calendrier=calendrier hébraïque, t=it, usage privé=whatever)" + ); + } } diff --git a/rust_icu_ustring/src/lib.rs b/rust_icu_ustring/src/lib.rs index 203a0f73..100b55d1 100644 --- a/rust_icu_ustring/src/lib.rs +++ b/rust_icu_ustring/src/lib.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! # Implemuntation of the functions in the ICU4C `ustring.h` header. +//! # Implementation of the functions in the ICU4C `ustring.h` header. //! //! This is where the UTF-8 strings get converted back and forth to the UChar //! representation.