Skip to content

Commit

Permalink
Add C FFI for LocaleCanonicalizer (#772)
Browse files Browse the repository at this point in the history
* Specify lifetimes separately for LocaleCanonicalizer

* Add LocaleCanonicalizer FFI

* Address review feedback
  • Loading branch information
dminor authored Jun 10, 2021
1 parent 61b7083 commit acf3886
Show file tree
Hide file tree
Showing 7 changed files with 190 additions and 16 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 12 additions & 14 deletions components/locale_canonicalizer/src/locale_canonicalizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ pub enum CanonicalizationResult {
Unmodified,
}

pub struct LocaleCanonicalizer<'a> {
aliases: DataPayload<'a, 'a, AliasesV1Marker>,
likely_subtags: DataPayload<'a, 'a, LikelySubtagsV1Marker>,
pub struct LocaleCanonicalizer<'d, 's> {
aliases: DataPayload<'d, 's, AliasesV1Marker>,
likely_subtags: DataPayload<'d, 's, LikelySubtagsV1Marker>,
extension_keys: Vec<Key>,
}

Expand Down Expand Up @@ -123,25 +123,23 @@ macro_rules! maximize_locale {
}};
}

impl LocaleCanonicalizer<'_> {
impl<'d, 's> LocaleCanonicalizer<'d, 's> {
/// A constructor which takes a [`DataProvider`] and creates a [`LocaleCanonicalizer`].
pub fn new<'d, P>(provider: &P) -> Result<LocaleCanonicalizer<'d>, DataError>
pub fn new<P>(provider: &P) -> Result<LocaleCanonicalizer<'d, 's>, DataError>
where
P: DataProvider<'d, 'd, AliasesV1Marker>
+ DataProvider<'d, 'd, LikelySubtagsV1Marker>
P: DataProvider<'d, 's, AliasesV1Marker>
+ DataProvider<'d, 's, LikelySubtagsV1Marker>
+ ?Sized,
{
// The `rg` region override and `sd` regional subdivision keys may contain
// language codes that require canonicalization.
let extension_keys = vec![
Key::from_tinystr4_unchecked(tinystr4!("rg")),
Key::from_tinystr4_unchecked(tinystr4!("sd")),
];
let aliases: DataPayload<AliasesV1Marker> =
// The `rg` region override and `sd` regional subdivision keys may contain
// language codes that require canonicalization.
provider
.load_payload(&DataRequest::from(key::ALIASES_V1))?
.take_payload()?
;
let aliases: DataPayload<AliasesV1Marker> = provider
.load_payload(&DataRequest::from(key::ALIASES_V1))?
.take_payload()?;

let likely_subtags: DataPayload<LikelySubtagsV1Marker> = provider
.load_payload(&DataRequest::from(key::LIKELY_SUBTAGS_V1))?
Expand Down
1 change: 1 addition & 0 deletions ffi/capi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ path = "src/lib.rs"
[dependencies]
fixed_decimal = { path = "../../utils/fixed_decimal" }
icu_decimal = { path = "../../components/decimal/" }
icu_locale_canonicalizer = { path = "../../components/locale_canonicalizer" }
icu_locid = { path = "../../components/locid" }
icu_plurals = { path = "../../components/plurals/" }
icu_provider = { path = "../../provider/core" }
Expand Down
68 changes: 66 additions & 2 deletions ffi/capi/examples/locale/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,87 @@
// (online at: https:/unicode-org/icu4x/blob/main/LICENSE ).

#include "../../include/locale.h"
#include "../../include/locale_canonicalizer.h"
#include <string.h>
#include <stdio.h>

const char* path = "../../../../provider/testdata/data/json/";
int main() {
char output[40];
ICU4XWriteable write = icu4x_simple_writeable(output, 40);

// Test creating a locale.
ICU4XWriteable write = icu4x_simple_writeable(output, 40);
ICU4XLocale* locale = icu4x_locale_create("ar", 2);
bool success = icu4x_locale_tostring(locale, &write);
if (!success) {
return 1;
}
const char* expected = u8"ar";
if (strcmp(output, expected) != 0) {
printf("Output does not match expected output!\n");
return 1;
}
printf("Output is %s\n", output);
icu4x_locale_destroy(locale);

// Create a LocaleCanonicalizer.
ICU4XCreateDataProviderResult result = icu4x_fs_data_provider_create(path, strlen(path));
if (!result.success) {
printf("Failed to create FsDataProvider\n");
return 1;
}
ICU4XDataProvider provider = result.provider;
ICU4XLocaleCanonicalizer* lc = icu4x_localecanonicalizer_create(&provider);

// expect "ar"
// Test maximize.
write = icu4x_simple_writeable(output, 40);
locale = icu4x_locale_create("und", 3);
icu4x_localecanonicalizer_maximize(lc, locale);
success = icu4x_locale_tostring(locale, &write);
if (!success) {
return 1;
}
expected = u8"en-Latn-US";
if (strcmp(output, expected) != 0) {
printf("Output does not match expected output!\n");
return 1;
}
printf("Output is %s\n", output);
icu4x_locale_destroy(locale);

// Test minimize.
write = icu4x_simple_writeable(output, 40);
locale = icu4x_locale_create("zh-Hant", 7);
icu4x_localecanonicalizer_minimize(lc, locale);
success = icu4x_locale_tostring(locale, &write);
if (!success) {
return 1;
}
expected = u8"zh-TW";
if (strcmp(output, expected) != 0) {
printf("Output does not match expected output!\n");
return 1;
}
printf("Output is %s\n", output);
icu4x_locale_destroy(locale);

// Test canonicalize.
write = icu4x_simple_writeable(output, 40);
locale = icu4x_locale_create("no-nynorsk", 10);
icu4x_localecanonicalizer_canonicalize(lc, locale);
success = icu4x_locale_tostring(locale, &write);
if (!success) {
return 1;
}
expected = u8"nn";
if (strcmp(output, expected) != 0) {
printf("Output does not match expected output!\n");
return 1;
}
printf("Output is %s\n", output);
icu4x_locale_destroy(locale);

icu4x_localecanonicalizer_destroy(lc);

return 0;
}
32 changes: 32 additions & 0 deletions ffi/capi/include/locale_canonicalizer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https:/unicode-org/icu4x/blob/main/LICENSE ).

#ifndef ICU4X_LOCALE_CANONICALIZER_H
#define ICU4X_LOCALE_CANONICALIZER_H

#include "provider.h"
#include "locale.h"

typedef enum {
ICU4XCanonicalizationResult_Modified,
ICU4XCanonicalizationResult_Unmodified,
} ICU4XCanonicalizationResult;

// opaque
typedef struct ICU4XLocaleCanonicalizer ICU4XLocaleCanonicalizer;

ICU4XLocaleCanonicalizer* icu4x_localecanonicalizer_create(const ICU4XDataProvider* provider);

ICU4XCanonicalizationResult
icu4x_localecanonicalizer_canonicalize(const ICU4XLocaleCanonicalizer* lc, ICU4XLocale* locale);

ICU4XCanonicalizationResult
icu4x_localecanonicalizer_maximize(const ICU4XLocaleCanonicalizer* lc, ICU4XLocale* locale);

ICU4XCanonicalizationResult
icu4x_localecanonicalizer_minimize(const ICU4XLocaleCanonicalizer* lc, ICU4XLocale* locale);

void icu4x_localecanonicalizer_destroy(ICU4XLocaleCanonicalizer* lc);

#endif // ICU4X_LOCALE_CANONICALIZER_H
1 change: 1 addition & 0 deletions ffi/capi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub mod custom_writeable;
pub mod decimal;
pub mod fixed_decimal;
pub mod locale;
pub mod locale_canonicalizer;
pub mod pluralrules;
pub mod provider;

Expand Down
77 changes: 77 additions & 0 deletions ffi/capi/src/locale_canonicalizer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https:/unicode-org/icu4x/blob/main/LICENSE ).

use crate::locale::ICU4XLocale;
use crate::provider::ICU4XDataProvider;
use icu_locale_canonicalizer::{CanonicalizationResult, LocaleCanonicalizer};
use std::ptr;

/// Opaque type for use behind a pointer, is [`LocaleCanonicalizer`]
///
/// Can be obtained via [`icu4x_localecanonicalizer_create()`] and
/// destroyed via [`icu4x_localecanonicalizer_destroy()`]
pub type ICU4XLocaleCanonicalizer<'d, 's> = LocaleCanonicalizer<'d, 'static>;

#[repr(C)]
c_enum! {
/// FFI version of [`CanonicalizationResult`]. See its docs for more details.
pub c_enum ICU4XCanonicalizationResult is CanonicalizationResult {
Modified,
Unmodified,
}
}

#[no_mangle]
/// FFI version of [`LocaleCanonicalizer::new()`], see its docs for more details
///
/// # Safety
/// - `provider` should be constructed via one of the functions in [`crate::provider`](crate::provider)
pub unsafe extern "C" fn icu4x_localecanonicalizer_create(
provider: &ICU4XDataProvider,
) -> *mut ICU4XLocaleCanonicalizer {
let provider = provider.as_dyn_ref();
if let Ok(lc) = ICU4XLocaleCanonicalizer::new(provider) {
let boxed = Box::new(lc);
Box::into_raw(boxed)
} else {
ptr::null_mut()
}
}

#[no_mangle]
/// FFI version of [`LocaleCanonicalizer::canonicalize()`]. See its docs for more details.
pub extern "C" fn icu4x_localecanonicalizer_canonicalize(
lc: &ICU4XLocaleCanonicalizer,
locale: &mut ICU4XLocale,
) -> ICU4XCanonicalizationResult {
lc.canonicalize(locale).into()
}

#[no_mangle]
/// FFI version of [`LocaleCanonicalizer::maximize()`]. See its docs for more details.
pub extern "C" fn icu4x_localecanonicalizer_maximize(
lc: &ICU4XLocaleCanonicalizer,
locale: &mut ICU4XLocale,
) -> ICU4XCanonicalizationResult {
lc.maximize(locale).into()
}

#[no_mangle]
/// FFI version of [`LocaleCanonicalizer::minimize()`]. See its docs for more details.
pub extern "C" fn icu4x_localecanonicalizer_minimize(
lc: &ICU4XLocaleCanonicalizer,
locale: &mut ICU4XLocale,
) -> ICU4XCanonicalizationResult {
lc.minimize(locale).into()
}

#[no_mangle]
/// Destructor for [`ICU4XLocaleCanonicalizer`].
///
/// # Safety
///
/// `lc` must be a pointer to a locale allocated by `icu4x_localecanonicalizer_create`.
pub unsafe extern "C" fn icu4x_localecanonicalizer_destroy(lc: *mut ICU4XLocaleCanonicalizer) {
let _ = Box::from_raw(lc);
}

0 comments on commit acf3886

Please sign in to comment.