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

Add basic C API for PluralRules #552

Merged
merged 12 commits into from
Mar 19, 2021
10 changes: 10 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ members = [
"experimental/segmenter_lstm",
"components/datetime",
"components/ecma402",
"components/capi",
"components/icu",
"components/icu4x",
"components/locale_canonicalizer",
Expand Down
9 changes: 9 additions & 0 deletions Makefile.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ command = "cargo"
# Note: we need test-all-features (rather than build-all-features) for docs
args = ["test-all-features"]

[tasks.test-capi]
description = "Run C API tests"
category = "ICU4X Development"
script = '''
cd components/capi/examples/pluralrules;
make
'''

[tasks.license-header-check]
description = "Ensure all the source files have license headers"
category = "ICU4X Development"
Expand Down Expand Up @@ -56,6 +64,7 @@ dependencies = [
"fmt-check",
"clippy-all",
"license-header-check",
"test-capi",
]

[tasks.ci]
Expand Down
20 changes: 20 additions & 0 deletions components/capi/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# 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://github.com/unicode-org/icu4x/blob/main/LICENSE ).

[package]
name = "icu_capi"
version = "0.1.0"
authors = ["The ICU4X Developers"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[lib]
crate-type = ["staticlib", "rlib"]

[dependencies]
icu_locid = { path = "../locid" }
icu_plurals = { path = "../plurals/" }
icu_provider = { path = "../provider" }
icu_provider_fs = { path = "../provider_fs/" }
1 change: 1 addition & 0 deletions components/capi/examples/pluralrules/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a.out
25 changes: 25 additions & 0 deletions components/capi/examples/pluralrules/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# 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://github.com/unicode-org/icu4x/blob/main/LICENSE ).

.DEFAULT_GOAL := test
.PHONY: build test

ALL_HEADERS := $(wildcard ../../include/*.h)
ALL_RUST := $(wildcard ../../src/*.rs)

$(ALL_RUST):

$(ALL_HEADERS):


../../../../target/debug/libicu_capi.a: $(ALL_RUST)
cargo build

a.out: ../../../../target/debug/libicu_capi.a $(ALL_HEADERS) test.c
gcc test.c ../../../../target/debug/libicu_capi.a -ldl -lpthread -lm

build: a.out

test: build
./a.out
40 changes: 40 additions & 0 deletions components/capi/examples/pluralrules/test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// 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://github.com/unicode-org/icu4x/blob/main/LICENSE ).

#include "../../include/pluralrules.h"
#include <string.h>
#include <stdio.h>

const char* path = "../../../../resources/testdata/data/json/";
int main() {
ICU4XLocale* locale = icu4x_locale_create("ar", 2);
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;
ICU4XCreatePluralRulesResult plural_result = icu4x_plural_rules_create(locale, &provider, ICU4XPluralRuleType_Cardinal);
if (!plural_result.success) {
printf("Failed to create PluralRules\n");
return 1;
}
ICU4XPluralRules* rules = plural_result.rules;

ICU4XPluralOperands op;
op.i = 3;

ICU4XPluralCategory cat = icu4x_plural_rules_select(rules, &op);

printf("Plural Category %d (should be %d)\n", (int)cat, (int)ICU4XPluralCategory_Few);

icu4x_plural_rules_destroy(rules);
icu4x_data_provider_destroy(provider);
icu4x_locale_destroy(locale);

if (cat != ICU4XPluralCategory_Few) {
return 1;
}
return 0;
}
18 changes: 18 additions & 0 deletions components/capi/include/locale.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// 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://github.com/unicode-org/icu4x/blob/main/LICENSE ).

#ifndef ICU4X_LOCALE_H
#define ICU4X_LOCALE_H

#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>

// opaque
typedef struct ICU4XLocale ICU4XLocale;

ICU4XLocale* icu4x_locale_create(const char* value, size_t len);
void icu4x_locale_destroy(ICU4XLocale*);

#endif // ICU4X_LOCALE_H
50 changes: 50 additions & 0 deletions components/capi/include/pluralrules.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// 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://github.com/unicode-org/icu4x/blob/main/LICENSE ).

#ifndef ICU4X_PLURALRULES_H
#define ICU4X_PLURALRULES_H

#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include "provider.h"
#include "locale.h"

// opaque
typedef struct ICU4XPluralRules ICU4XPluralRules;

typedef struct {
ICU4XPluralRules* rules;
bool success;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So in this case, a nullptr for rules could indicate failure, so success is redundant. If results are always going to be ok or error, we could just use the pointer to indicate this. If not, then maybe we should have an enum of possible results, which could just be Ok/Error for now.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking of adding a proper error enum to this later, the success is just a placeholder.

} ICU4XCreatePluralRulesResult;

typedef enum {
ICU4XPluralRuleType_Cardinal,
ICU4XPluralRuleType_Ordinal
} ICU4XPluralRuleType;

typedef enum {
ICU4XPluralCategory_Zero,
ICU4XPluralCategory_One,
ICU4XPluralCategory_Two,
ICU4XPluralCategory_Few,
ICU4XPluralCategory_Many,
ICU4XPluralCategory_Other,
} ICU4XPluralCategory;

typedef struct {
uint64_t i;
size_t v;
size_t w;
uint64_t f;
uint64_t t;
size_t c;
} ICU4XPluralOperands;


ICU4XCreatePluralRulesResult icu4x_plural_rules_create(const ICU4XLocale* locale, const ICU4XDataProvider* provider, ICU4XPluralRuleType ty);
ICU4XPluralCategory icu4x_plural_rules_select(const ICU4XPluralRules* rules, const ICU4XPluralOperands* op);
void icu4x_plural_rules_destroy(ICU4XPluralRules* rules);

#endif // ICU4X_PLURALRULES_H
26 changes: 26 additions & 0 deletions components/capi/include/provider.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// 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://github.com/unicode-org/icu4x/blob/main/LICENSE ).

#ifndef ICU4X_PROVIDER_H
#define ICU4X_PROVIDER_H

#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>

typedef struct {
uintptr_t _field1;
uintptr_t _field2;
} ICU4XDataProvider;

typedef struct {
ICU4XDataProvider provider;
bool success;
} ICU4XCreateDataProviderResult;

void icu4x_data_provider_destroy(ICU4XDataProvider d);

ICU4XCreateDataProviderResult icu4x_fs_data_provider_create(const char* path, size_t len);

#endif // ICU4X_PROVIDER_H
7 changes: 7 additions & 0 deletions components/capi/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// 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://github.com/unicode-org/icu4x/blob/main/LICENSE ).

pub mod locale;
pub mod pluralrules;
pub mod provider;
37 changes: 37 additions & 0 deletions components/capi/src/locale.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// 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://github.com/unicode-org/icu4x/blob/main/LICENSE ).

use std::slice;

use icu_locid::Locale;

/// Opaque type for use behind a pointer, is [`Locale`]
///
/// Can be obtained via [`icu4x_locale_create()`] and destroyed via [`icu4x_locale_destroy()`]
pub type ICU4XLocale = Locale;

#[no_mangle]
/// FFI version of [`Locale::from_bytes()`], see its docs for more details
///
/// # Safety
/// `value` and `len` should point to a valid ASCII string of length `len`.
///
/// It does not need to be be null terminated, and `len` should not include a null
/// terminator (this will just cause the function to panic, and is not a safety requirement).
pub unsafe extern "C" fn icu4x_locale_create(value: *const u8, len: usize) -> *mut ICU4XLocale {
let bytes = slice::from_raw_parts(value, len);
// todo: return errors
let loc = ICU4XLocale::from_bytes(bytes).unwrap();
Box::into_raw(Box::new(loc))
}

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