Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Implement uloc_openAvailableByType() #295

Merged
merged 6 commits into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,4 @@ jobs:
- if: matrix.runs-on == 'macos-latest'
run: make macos-test
- if: matrix.runs-on == 'ubuntu-latest'
run: make test
run: make test
4 changes: 2 additions & 2 deletions coverage/report.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
| `uenum.h` | 3 / 8 |
| `uformattable.h` | 6 / 13 |
| `ulistformatter.h` | 2 / 8 |
| `uloc.h` | 28 / 42 |
| `uloc.h` | 29 / 42 |
| `umsg.h` | 5 / 20 |
| `unum.h` | 14 / 32 |
| `unumberformatter.h` | 3 / 6 |
Expand Down Expand Up @@ -284,6 +284,7 @@
| | `uloc_getScript` |
| | `uloc_getVariant` |
| | `uloc_minimizeSubtags` |
| | `uloc_openAvailableByType` |
| | `uloc_openKeywords` |
| | `uloc_setDefault` |
| | `uloc_toLanguageTag` |
Expand All @@ -301,7 +302,6 @@
| `uloc_getLocaleForLCID` | |
| `uloc_getParent` | |
| `uloc_isRightToLeft` | |
| `uloc_openAvailableByType` | |
| `uloc_setKeywordValue` | |
| `uloc_toLegacyType` | |

Expand Down
1 change: 1 addition & 0 deletions coverage/uloc_implemented.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ uloc_getName
uloc_getScript
uloc_getVariant
uloc_minimizeSubtags
uloc_openAvailableByType
uloc_openKeywords
uloc_setDefault
uloc_toLanguageTag
Expand Down
2 changes: 1 addition & 1 deletion rust_icu_uloc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ rust_icu_common = { path = "../rust_icu_common", version = "4.2.3", default-feat
rust_icu_sys = { path = "../rust_icu_sys", version = "4.2.3", default-features = false }
rust_icu_uenum = { path = "../rust_icu_uenum", version = "4.2.3", default-features = false }
rust_icu_ustring = { path = "../rust_icu_ustring", version = "4.2.3", default-features = false }
anyhow = "1.0.25"

[dev-dependencies]
anyhow = "1.0.25"

# See the feature description in ../rust_icu_sys/Cargo.toml for details.
[features]
Expand Down
132 changes: 107 additions & 25 deletions rust_icu_uloc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// limitations under the License.

use {
anyhow::anyhow,
rust_icu_common as common,
rust_icu_common::buffered_string_method_with_retry,
rust_icu_sys as sys,
Expand All @@ -27,7 +28,6 @@ use {
convert::{From, TryFrom, TryInto},
ffi, fmt,
os::raw,
sync::OnceLock,
},
};

Expand Down Expand Up @@ -369,20 +369,49 @@ impl ULoc {
}

/// Implements `uloc_getAvailable`.
pub fn get_available() -> &'static Vec<ULoc> {
static LOCALES: OnceLock<Vec<ULoc>> = OnceLock::new();
LOCALES.get_or_init(|| {
let count = ULoc::count_available();
let mut vec = Vec::with_capacity(count as usize);
let mut index: i32 = 0;
while index < count {
let label = unsafe { ffi::CStr::from_ptr(versioned_function!(uloc_getAvailable)(index)).to_str().unwrap() };
let locale = ULoc::try_from(label).unwrap();
vec.push(locale);
index += 1;
}
vec
})
pub fn get_available(n: i32) -> Result<ULoc, common::Error> {
if (0 > n) || (n >= Self::count_available()) {
panic!("{} is negative or greater than or equal to the value returned from count_available()", n);
}
let ptr = unsafe { versioned_function!(uloc_getAvailable)(n) };
if ptr == std::ptr::null() {
return Err(common::Error::Wrapper(anyhow!("uloc_getAvailable() returned a null pointer")));
}
let label = unsafe { ffi::CStr::from_ptr(ptr).to_str().unwrap() };
ULoc::try_from(label)
}

/// Returns a vector of available locales
pub fn get_available_locales() -> Vec<ULoc> {
let count = ULoc::count_available();
let mut vec = Vec::with_capacity(count as usize);
let mut index: i32 = 0;
while index < count {
let locale = Self::get_available(index).unwrap();
vec.push(locale);
index += 1;
}
vec
}

/// Implements `uloc_openAvailableByType`.
#[cfg(feature = "icu_version_67_plus")]
pub fn open_available_by_type(locale_type: ULocAvailableType) -> Result<Enumeration, common::Error> {
let mut status = common::Error::OK_CODE;
unsafe {
let iter = Enumeration::from_raw_parts(None, versioned_function!(uloc_openAvailableByType)(locale_type, &mut status));
common::Error::ok_or_warning(status)?;
Ok(iter)
}
}

/// Returns a vector of locales of the requested type.
#[cfg(feature = "icu_version_67_plus")]
pub fn get_available_locales_by_type(locale_type: ULocAvailableType) -> Vec<ULoc> {
if locale_type == ULocAvailableType::ULOC_AVAILABLE_COUNT {
panic!("ULOC_AVAILABLE_COUNT is for internal use only");
}
Self::open_available_by_type(locale_type).unwrap().map(|x| ULoc::try_from(x.unwrap().as_str()).unwrap()).collect()
}

/// Implements `uloc_addLikelySubtags` from ICU4C.
Expand Down Expand Up @@ -800,7 +829,7 @@ mod tests {

// This test yields a different result in ICU versions prior to 64:
// "zh-Latn@collation=pinyin".
#[cfg(features = "icu_version_64_plus")]
#[cfg(feature = "icu_version_64_plus")]
#[test]
fn test_variant() -> Result<(), Error> {
let loc = ULoc::try_from("zh-Latn-pinyin")?;
Expand All @@ -820,7 +849,7 @@ mod tests {
Ok(())
}

#[cfg(features = "icu_version_64_plus")]
#[cfg(feature = "icu_version_64_plus")]
#[test]
fn test_name() -> Result<(), Error> {
let loc = ULoc::try_from("en-US")?;
Expand Down Expand Up @@ -1187,15 +1216,15 @@ mod tests {
assert_eq!(ULoc::for_language_tag("sr-u-tz-uslax").unwrap(), loc);
}

#[cfg(features = "icu_version_64_plus")]
#[cfg(feature = "icu_version_64_plus")]
#[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);
}

#[cfg(features = "icu_version_64_plus")]
#[cfg(feature = "icu_version_64_plus")]
#[test]
fn test_iso3_language() {
let loc = ULoc::for_language_tag("en-US").unwrap();
Expand All @@ -1206,7 +1235,7 @@ mod tests {
assert_eq!(iso_lang, None);
}

#[cfg(features = "icu_version_64_plus")]
#[cfg(feature = "icu_version_64_plus")]
#[test]
fn test_iso3_country() {
let loc = ULoc::for_language_tag("en-US").unwrap();
Expand All @@ -1217,7 +1246,7 @@ mod tests {
assert_eq!(iso_country, None);
}

#[cfg(features = "icu_version_64_plus")]
#[cfg(feature = "icu_version_64_plus")]
#[test]
fn test_display_language() {
let english_locale = ULoc::for_language_tag("en").unwrap();
Expand All @@ -1231,7 +1260,7 @@ mod tests {
assert_eq!(root_locale.display_language(&french_locale).unwrap().as_string_debug(), "langue indéterminée");
}

#[cfg(features = "icu_version_64_plus")]
#[cfg(feature = "icu_version_64_plus")]
#[test]
fn test_display_script() {
let english_latin_locale = ULoc::for_language_tag("en-latg").unwrap();
Expand Down Expand Up @@ -1272,7 +1301,7 @@ mod tests {
assert_eq!(calendar_value_in_french.unwrap().as_string_debug(), "calendrier hébraïque");
}

#[cfg(features = "icu_version_64_plus")]
#[cfg(feature = "icu_version_64_plus")]
#[test]
fn test_display_name() {
let loc = ULoc::for_language_tag("az-Cyrl-AZ-u-ca-hebrew-t-it-x-whatever").unwrap();
Expand All @@ -1284,14 +1313,67 @@ mod tests {
"azerbaïdjanais (cyrillique, Azerbaïdjan, calendrier=calendrier hébraïque, t=it, usage privé=whatever)"
);
}

#[test]
#[should_panic(expected = "-1 is negative or greater than or equal to the value returned from count_available()")]
fn test_get_available_negative() {
let _ = ULoc::get_available(-1);
}

#[test]
fn test_get_available() {
let locales = ULoc::get_available();
fn test_get_available_overrun() {
let index = ULoc::count_available();
let result = std::panic::catch_unwind(|| ULoc::get_available(index));
assert!(result.is_err());
}

#[test]
fn test_get_available_locales() {
let locales = ULoc::get_available_locales();
assert!(locales.contains(&ULoc::try_from("en").unwrap()));
assert!(locales.contains(&ULoc::try_from("en-US").unwrap()));
assert!(locales.contains(&ULoc::try_from("fr").unwrap()));
assert!(locales.contains(&ULoc::try_from("fr-FR").unwrap()));
assert_eq!(ULoc::count_available() as usize, locales.capacity());
assert_eq!(locales.len(), locales.capacity());
}

#[cfg(feature = "icu_version_67_plus")]
#[test]
fn test_get_available_locales_by_type() {
let locales1 = ULoc::get_available_locales_by_type(ULocAvailableType::ULOC_AVAILABLE_DEFAULT);
let locales2 = ULoc::get_available_locales();
assert_eq!(locales1, locales2);
let alias_locales = ULoc::get_available_locales_by_type(ULocAvailableType::ULOC_AVAILABLE_ONLY_LEGACY_ALIASES);
let all_locales = ULoc::get_available_locales_by_type(ULocAvailableType::ULOC_AVAILABLE_WITH_LEGACY_ALIASES);
for locale in &alias_locales {
assert!(all_locales.contains(&locale));
assert!(!locales1.contains(&locale));
}
for locale in &locales1 {
assert!(all_locales.contains(&locale));
assert!(!alias_locales.contains(&locale));
}
assert!(alias_locales.len() > 0);
assert!(locales1.len() > alias_locales.len());
assert!(all_locales.len() > locales1.len());
assert!(alias_locales.contains(&ULoc::try_from("iw").unwrap()));
assert!(alias_locales.contains(&ULoc::try_from("mo").unwrap()));
assert!(alias_locales.contains(&ULoc::try_from("zh-CN").unwrap()));
assert!(alias_locales.contains(&ULoc::try_from("sr-BA").unwrap()));
assert!(alias_locales.contains(&ULoc::try_from("ars").unwrap()));
}

#[cfg(feature = "icu_version_67_plus")]
#[test]
#[should_panic(expected = "ULOC_AVAILABLE_COUNT is for internal use only")]
fn test_get_available_locales_by_type_panic() {
ULoc::get_available_locales_by_type(ULocAvailableType::ULOC_AVAILABLE_COUNT);
}

#[cfg(feature = "icu_version_67_plus")]
#[test]
fn test_get_available_locales_by_type_error() {
assert!(!ULoc::open_available_by_type(ULocAvailableType::ULOC_AVAILABLE_COUNT).is_ok());
}
}