Skip to content

Commit

Permalink
Add basic C API for PluralRules (#552)
Browse files Browse the repository at this point in the history
* Add rust-side C API crate, with code for plural rules

* Add headers

* Add ctest

* Make some things const

* Rename ICU4XErasedDataProvider -> ICU4XDataProvider

* Replace into with from

* Add comments and docs

* rename icu4x_erased_data_provider_destroy -> icu4x_data_provider_destroy

* fmt

* Add cargo-make target for ctest

* Move pluralrules ctest to examples

* goodby #pragma once :-'(
  • Loading branch information
Manishearth authored Mar 19, 2021
1 parent 4dae1fa commit 91dab1c
Show file tree
Hide file tree
Showing 14 changed files with 517 additions and 0 deletions.
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;
} 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

0 comments on commit 91dab1c

Please sign in to comment.