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 JSON data provider with sample number data #61

Merged
merged 37 commits into from
Jun 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
3dd5d43
Add icu-util project
sffc Apr 28, 2020
7cef64a
Add icu-datap-json project
sffc Apr 28, 2020
bb1e1b5
Add an initial data provider trait definition
sffc Apr 28, 2020
52845b0
Improving no_std support; adding serde; adding datap_json
sffc Apr 28, 2020
996b397
Unit test for JSON-based data provider
sffc Apr 28, 2020
0147b24
Use dynamic type for response payload
sffc Apr 30, 2020
b495fcb
Remove icu_util::std
sffc Apr 30, 2020
7cff876
Remove obsolete code
sffc Apr 30, 2020
9d0298d
Rename payload2 to payload
sffc Apr 30, 2020
5950fdb
Auto-implement Bovine for all data traits
sffc May 1, 2020
457d8ad
Rename Bovine to ClonableAny
sffc May 1, 2020
1791deb
Additional polishing of Response struct
sffc May 1, 2020
dbb767d
Fix spelling of Cloneable
sffc May 1, 2020
070cc8b
Add no_std tests for JSON Data Provider
sffc May 6, 2020
1ed302a
Adding take_payload, plus other cleanup
sffc May 6, 2020
4cd595a
Running cargo fmt
sffc May 6, 2020
727f40b
Running cargo fmt
sffc May 7, 2020
d8242ec
Merge remote-tracking branch 'upstream/master' into decimal
sffc May 15, 2020
d55f1fd
Remove unused unstable feature
sffc May 16, 2020
488fe22
Comments and refactoring.
sffc May 16, 2020
427c76d
Change payload functions to return Error instead of Option
sffc May 16, 2020
6dd1f34
Initial code review feedback
sffc May 20, 2020
50baf26
Merge remote-tracking branch 'upstream/master' into decimal
sffc May 20, 2020
a68b90a
Merge master to decimal
sffc May 22, 2020
d8b7b12
Add lifetime parameter to DataProvider.
sffc May 27, 2020
d95b1fc
Removing Request from Response object.
sffc May 28, 2020
40205d9
Upgrade JsonDataProvider to returned borrowed data
sffc May 28, 2020
08ac7f1
Allow strings in SymbolsV1 to be static
sffc May 29, 2020
2194dbf
Add backreference
sffc May 29, 2020
0d6828a
Coverage
sffc Jun 6, 2020
25a806e
New crate names.
sffc Jun 6, 2020
e738a9f
Add key From impl
sffc Jun 6, 2020
1ca3440
Merge remote-tracking branch 'upstream/master' into decimal
sffc Jun 6, 2020
82af10e
Running cargo fmt
sffc Jun 6, 2020
56d678e
Use LanguageIdentifier instead of String
sffc Jun 6, 2020
d3a7d36
Rename directories to kebab case
sffc Jun 9, 2020
c663a31
Use LanguageIdentifier::default()
sffc Jun 9, 2020
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: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
[workspace]

members = [
"components/data-provider",
"components/data-provider-json",
"components/icu",
"components/icu4x",
"components/locale",
Expand Down
45 changes: 45 additions & 0 deletions components/data-provider-json/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
[package]
name = "icu-data-provider-json"
description = "ICU4X data provider that reads from JSON files"
version = "0.0.1"
authors = ["The ICU4X Project Developers"]
edition = "2018"
readme = "README.md"
repository = "https://github.com/unicode-org/icu4x"
license = "MIT/Apache-2.0"
categories = ["internationalization"]
include = [
"src/**/*",
"Cargo.toml",
"README.md"
]

[features]
default = ["std"]
std = ["no-std-compat/std", "serde/std", "serde_json/std"]

[dependencies]
icu-data-provider = { path = "../data-provider" }
icu-locale = { path = "../locale" }

[dependencies.async-trait]
version = "0.1.30"

[dependencies.serde]
version = "1.0"
default-features = false
features = ["derive", "alloc"]

[dependencies.serde_json]
version = "1.0"
default-features = false
features = ["alloc"]

[dependencies.no-std-compat]
version = "0.4.0"
features = ["alloc"]

[[test]]
name = "file_io"
path = "tests/test_file_io.rs"
required-features = ["std"]
11 changes: 11 additions & 0 deletions components/data-provider-json/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# ICU4X

ICU4X is a set of internationalization components for Unicode.

# Status [![crates.io](http://meritbadge.herokuapp.com/icu-util)](https://crates.io/crates/icu-util)

The project is in an incubation period.

# Authors

The project is managed by a subcommittee of ICU-TC in the Unicode Consortium focused on providing solutions for client-side internationalization.
79 changes: 79 additions & 0 deletions components/data-provider-json/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#![no_std]

extern crate no_std_compat as std;

use std::prelude::v1::*;

use icu_data_provider::DataProvider;
use icu_data_provider::Request;
use icu_data_provider::Response;
use icu_data_provider::ResponseBuilder;
use icu_data_provider::ResponseError;
use icu_locale::LanguageIdentifier;

mod schema;

#[cfg(feature = "std")]
use std::io::Read;

#[derive(Debug)]
pub enum Error {
JsonError(serde_json::error::Error),
}

impl From<serde_json::error::Error> for Error {
fn from(err: serde_json::error::Error) -> Error {
Error::JsonError(err)
}
}

/// A data provider reading from a JSON file.
#[derive(Debug)]
pub struct JsonDataProvider {
data: schema::JsonSchema,
}

impl JsonDataProvider {
/// Create a JsonDataProvider from a file reader.
#[cfg(feature = "std")]
pub fn from_reader<R: Read>(reader: R) -> Result<Self, Error> {
let result: schema::JsonSchema = serde_json::from_reader(reader)?;
Ok(JsonDataProvider { data: result })
}

/// Create a JsonDataProvider from a JSON string slice.
pub fn from_str(data: &str) -> Result<Self, Error> {
let result: schema::JsonSchema = serde_json::from_str(data)?;
Ok(JsonDataProvider { data: result })
}

/// Create a JsonDataProvider from a &[u8] slice.
pub fn from_slice(data: &[u8]) -> Result<Self, Error> {
let result: schema::JsonSchema = serde_json::from_slice(data)?;
Ok(JsonDataProvider { data: result })
}
}

impl<'a> DataProvider<'a, 'a> for JsonDataProvider {
/// Loads JSON data. Returns borrowed data.
fn load(&'a self, _request: &Request) -> Result<Response<'a>, ResponseError> {
let response = ResponseBuilder {
data_langid: LanguageIdentifier::default(),
}
.with_borrowed_payload(&self.data.decimal.symbols_v1_a);
Ok(response)
}
}

#[test]
fn test_empty_str() {
let result = JsonDataProvider::from_str("");
assert!(result.is_err());
let err = result.unwrap_err();
// Coverage for Debug trait:
println!("{:?}", err);
// An unconditional let is possible here because it is a one-element enum.
// If more cases are needed, see https://github.com/rust-lang/rfcs/pull/1303
let Error::JsonError(json_err) = err;
assert_eq!(json_err.classify(), serde_json::error::Category::Eof);
}
15 changes: 15 additions & 0 deletions components/data-provider-json/src/schema.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use icu_data_provider as datap;
use serde::{Deserialize, Serialize};

#[allow(unused_imports)]
use std::prelude::v1::*;

#[derive(Serialize, Deserialize, Debug)]
pub(crate) struct DecimalJsonSchema {
pub(crate) symbols_v1_a: datap::decimal::SymbolsV1,
}

#[derive(Serialize, Deserialize, Debug)]
pub(crate) struct JsonSchema {
pub(crate) decimal: DecimalJsonSchema,
}
35 changes: 35 additions & 0 deletions components/data-provider-json/tests/test_file_io.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use icu_data_provider_json;

use std::borrow::Cow;

use std::fs::File;
use std::io::BufReader;

use icu_data_provider as datap;
use icu_data_provider::DataProvider;
use icu_data_provider_json::JsonDataProvider;

#[test]
fn test_read_json() {
let file = File::open("tests/testdata/all.json").unwrap();
let reader = BufReader::new(file);
let json_data_provider = JsonDataProvider::from_reader(reader).unwrap();
println!("{:?}", json_data_provider); // Coverage for Debug trait
let response = json_data_provider
.load(&datap::Request {
langid: "en-US".parse().unwrap(),
category: datap::Category::Decimal,
key: datap::decimal::Key::SymbolsV1.into(),
payload: None,
})
.unwrap();
let decimal_data: &datap::decimal::SymbolsV1 = response.borrow_payload().unwrap();
assert_eq!(
decimal_data,
&datap::decimal::SymbolsV1 {
zero_digit: '0',
decimal_separator: Cow::Borrowed("."),
grouping_separator: Cow::Borrowed(","),
}
);
}
87 changes: 87 additions & 0 deletions components/data-provider-json/tests/test_no_std.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
extern crate icu_data_provider_json;

use std::borrow::Cow;
use std::prelude::v1::*;

use icu_data_provider as datap;
use icu_data_provider::DataProvider;
use icu_data_provider_json::JsonDataProvider;

const DATA: &'static str = r#"{
"decimal": {
"symbols_v1_a": {
"zero_digit": "0",
"decimal_separator": ".",
"grouping_separator": ","
}
}
}"#;

fn get_provider() -> JsonDataProvider {
JsonDataProvider::from_str(DATA).unwrap()
}

fn get_response(provider: &JsonDataProvider) -> datap::Response {
return provider
.load(&datap::Request {
langid: "en-US".parse().unwrap(),
category: datap::Category::Decimal,
key: datap::decimal::Key::SymbolsV1.into(),
payload: None,
})
.unwrap();
}

fn check_data(decimal_data: &datap::decimal::SymbolsV1) {
assert_eq!(
decimal_data,
&datap::decimal::SymbolsV1 {
zero_digit: '0',
decimal_separator: Cow::Borrowed("."),
grouping_separator: Cow::Borrowed(","),
}
);
}

#[test]
fn test_read_string() {
let provider = get_provider();
let response = get_response(&provider);
let decimal_data: &datap::decimal::SymbolsV1 = response.borrow_payload().unwrap();
check_data(decimal_data);
}

#[test]
fn test_read_utf8() {
let provider = JsonDataProvider::from_slice(DATA.as_bytes()).unwrap();
let response = get_response(&provider);
let decimal_data: &datap::decimal::SymbolsV1 = response.borrow_payload().unwrap();
check_data(decimal_data);
}

#[test]
fn test_borrow_payload_mut() {
let provider = get_provider();
let mut response = get_response(&provider);
let decimal_data: &mut datap::decimal::SymbolsV1 = response.borrow_payload_mut().unwrap();
check_data(decimal_data);
}

#[test]
fn test_take_payload() {
let provider = get_provider();
let response = get_response(&provider);
let decimal_data: Cow<datap::decimal::SymbolsV1> = response.take_payload().unwrap();
check_data(&decimal_data);
}

#[test]
fn test_clone_payload() {
let final_data = {
let provider = get_provider();
let response = get_response(&provider);
let decimal_data: Cow<datap::decimal::SymbolsV1> = response.take_payload().unwrap();
decimal_data.into_owned()
};
check_data(&final_data);
}
9 changes: 9 additions & 0 deletions components/data-provider-json/tests/testdata/all.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"decimal": {
"symbols_v1_a": {
"zero_digit": "0",
"decimal_separator": ".",
"grouping_separator": ","
}
}
}
37 changes: 37 additions & 0 deletions components/data-provider/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[package]
name = "icu-data-provider"
description = "Trait and struct definitions for the ICU data provider"
version = "0.0.1"
authors = ["The ICU4X Project Developers"]
edition = "2018"
readme = "README.md"
repository = "https://github.com/unicode-org/icu4x"
license = "MIT/Apache-2.0"
categories = ["internationalization"]
include = [
"src/**/*",
"Cargo.toml",
"README.md"
]

[features]
default = ["std"]
std = ["serde/std", "no-std-compat/std"]

[dependencies]
icu-locale = { path = "../locale" }

[dependencies.downcast-rs]
version = "1.1.1"

[dependencies.async-trait]
version = "0.1.30"

[dependencies.serde]
version = "1.0"
default-features = false
features = ["derive", "alloc"]

[dependencies.no-std-compat]
version = "0.4.0"
features = ["alloc"]
11 changes: 11 additions & 0 deletions components/data-provider/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# ICU4X

ICU4X is a set of internationalization components for Unicode.

# Status [![crates.io](http://meritbadge.herokuapp.com/icu-util)](https://crates.io/crates/icu-util)

The project is in an incubation period.

# Authors

The project is managed by a subcommittee of ICU-TC in the Unicode Consortium focused on providing solutions for client-side internationalization.
28 changes: 28 additions & 0 deletions components/data-provider/src/decimal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Decimal types

use std::prelude::v1::*;

use std::borrow::Cow;

use serde::{Deserialize, Serialize};

#[derive(PartialEq, Copy, Clone, Debug)]
pub enum Key {
SymbolsV1 = 1,
}

impl From<Key> for crate::Key {
fn from(value: Key) -> Self {
crate::Key::Decimal(value)
}
}

// TODO: de-duplicate the name "SymbolsV1" between Key and the struct
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
pub struct SymbolsV1 {
pub zero_digit: char,
// String ownership discussion:
// https://github.com/unicode-org/icu4x/pull/61#discussion_r429051895
pub decimal_separator: Cow<'static, str>,
pub grouping_separator: Cow<'static, str>,
}
Loading