-
Notifications
You must be signed in to change notification settings - Fork 183
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adding crate icu-cldr-json-data-provider.
- Loading branch information
Showing
12 changed files
with
1,431 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
[package] | ||
name = "icu-cldr-json-data-provider" | ||
description = "Data provider that reads from a CLDR JSON data source" | ||
version = "0.0.1" | ||
authors = ["The ICU4X Project Developers"] | ||
edition = "2018" | ||
readme = "README.md" | ||
repository = "https://github.com/unicode-org/icu4x" | ||
license-file = "../../LICENSE" | ||
categories = ["internationalization"] | ||
include = [ | ||
"src/**/*", | ||
"Cargo.toml", | ||
"README.md" | ||
] | ||
|
||
[dependencies] | ||
icu-data-provider = { path = "../data-provider" } | ||
icu-locale = { path = "../locale" } | ||
json = "0.12" | ||
serde = { version = "1.0", features = ["derive"] } | ||
serde_json = "1.0" | ||
serde-tuple-vec-map = "1.0" |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
use icu_locale::LanguageIdentifier; | ||
use serde::{Deserialize, Deserializer}; | ||
|
||
// TODO: Make this non-pub | ||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] | ||
pub struct CldrLanguage(pub LanguageIdentifier); | ||
|
||
impl<'de> Deserialize<'de> for CldrLanguage { | ||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | ||
where | ||
D: Deserializer<'de>, | ||
{ | ||
struct CldrLanguageVisitor; | ||
|
||
impl<'de> serde::de::Visitor<'de> for CldrLanguageVisitor { | ||
type Value = CldrLanguage; | ||
|
||
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
write!(formatter, "a valid Unicode Language Identifier or 'root'") | ||
} | ||
|
||
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> | ||
where | ||
E: serde::de::Error, | ||
{ | ||
if s == "root" { | ||
Ok(CldrLanguage("und".parse().unwrap())) | ||
} else { | ||
s.parse::<LanguageIdentifier>() | ||
.map(CldrLanguage) | ||
.map_err(serde::de::Error::custom) | ||
} | ||
} | ||
} | ||
|
||
deserializer.deserialize_string(CldrLanguageVisitor) | ||
} | ||
} | ||
|
||
#[test] | ||
fn deserialize() -> Result<(), Box<dyn std::error::Error>> { | ||
let fr = serde_json::from_str::<CldrLanguage>(r#""fr""#)?; | ||
let en = serde_json::from_str::<CldrLanguage>(r#""en-US""#)?; | ||
let root = serde_json::from_str::<CldrLanguage>(r#""root""#)?; | ||
|
||
assert_eq!(fr, CldrLanguage("fr".parse()?)); | ||
assert_eq!(en, CldrLanguage("en-US".parse()?)); | ||
assert_eq!(root, CldrLanguage("und".parse()?)); | ||
|
||
let failed = serde_json::from_str::<CldrLanguage>(r#""2Xs""#); | ||
assert!(failed.is_err()); | ||
let err = failed.unwrap_err(); | ||
assert!(err.is_data()); | ||
assert_eq!( | ||
err.to_string(), | ||
"The given language subtag is invalid at line 1 column 5".to_string() | ||
); | ||
|
||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
use crate::error::MissingSourceError; | ||
use std::default::Default; | ||
use std::path::PathBuf; | ||
|
||
/// Struct containing filesystem paths to the CLDR JSON resource directories. | ||
/// The fields should be Ok if present. They default to Err when not present. | ||
#[non_exhaustive] | ||
#[derive(Debug, PartialEq)] | ||
pub struct CldrPaths { | ||
/// Path to checkout of cldr-core: | ||
/// https://github.com/unicode-cldr/cldr-core | ||
pub cldr_core: Result<PathBuf, MissingSourceError>, | ||
} | ||
|
||
impl Default for CldrPaths { | ||
fn default() -> CldrPaths { | ||
CldrPaths { | ||
cldr_core: Err(MissingSourceError { src: "cldr-core" }), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
use std::error; | ||
use std::fmt; | ||
|
||
#[non_exhaustive] | ||
#[derive(Debug)] | ||
pub enum Error { | ||
JsonError(serde_json::error::Error), | ||
IoError(std::io::Error, std::path::PathBuf), | ||
MissingSource(MissingSourceError), | ||
} | ||
|
||
#[derive(Debug, PartialEq, Copy, Clone)] | ||
pub struct MissingSourceError { | ||
pub src: &'static str, | ||
} | ||
|
||
impl fmt::Display for MissingSourceError { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
write!(f, "Missing CLDR data source: {}", self.src) | ||
} | ||
} | ||
|
||
impl From<serde_json::error::Error> for Error { | ||
fn from(err: serde_json::error::Error) -> Self { | ||
Self::JsonError(err) | ||
} | ||
} | ||
|
||
impl From<MissingSourceError> for Error { | ||
fn from(err: MissingSourceError) -> Self { | ||
Self::MissingSource(err) | ||
} | ||
} | ||
|
||
impl fmt::Display for Error { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
match self { | ||
Error::JsonError(err) => write!(f, "{}", err), | ||
Error::IoError(err, path) => write!(f, "{}: {}", err, path.to_string_lossy()), | ||
Error::MissingSource(err) => err.fmt(f), | ||
} | ||
} | ||
} | ||
|
||
impl error::Error for Error { | ||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { | ||
match self { | ||
Error::JsonError(err) => Some(err), | ||
Error::IoError(err, _) => Some(err), | ||
_ => None, | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// #![feature(type_alias_impl_trait)] | ||
|
||
mod cldr_langid; | ||
mod cldr_paths; | ||
mod error; | ||
mod reader; | ||
mod support; | ||
|
||
pub mod transform; | ||
|
||
pub use cldr_paths::CldrPaths; | ||
pub use error::Error as CldrError; | ||
pub use transform::CldrJsonDataProvider; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
use crate::error::Error; | ||
use std::fs::File; | ||
use std::io::BufReader; | ||
use std::path::PathBuf; | ||
|
||
pub fn open_reader(path: PathBuf) -> Result<BufReader<File>, Error> { | ||
let file = match File::open(&path) { | ||
Ok(file) => file, | ||
Err(err) => return Err(Error::IoError(err, path)), | ||
}; | ||
Ok(BufReader::new(file)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
use crate::CldrPaths; | ||
use icu_data_provider::iter::DataEntryCollection; | ||
use icu_data_provider::prelude::*; | ||
use std::convert::TryFrom; | ||
use std::sync::RwLock; | ||
|
||
pub(crate) trait DataKeySupport { | ||
fn supports_key(data_key: &DataKey) -> Result<(), DataError>; | ||
} | ||
|
||
pub(crate) struct LazyCldrProvider<T> { | ||
data_provider: RwLock<Option<T>>, | ||
} | ||
|
||
impl<'b, 'd, T> LazyCldrProvider<T> | ||
where | ||
T: DataProvider<'d> + DataKeySupport + DataEntryCollection + TryFrom<&'b CldrPaths>, | ||
<T as TryFrom<&'b CldrPaths>>::Error: 'static + std::error::Error, | ||
{ | ||
pub fn new() -> Self { | ||
Self { | ||
data_provider: RwLock::new(None), | ||
} | ||
} | ||
|
||
pub fn try_load( | ||
&self, | ||
req: &DataRequest, | ||
cldr_paths: &'b CldrPaths, | ||
) -> Result<Option<DataResponse<'d>>, DataError> { | ||
if T::supports_key(&req.data_key).is_err() { | ||
return Ok(None); | ||
} | ||
if self.data_provider.read().unwrap().is_none() { | ||
self.data_provider.write().unwrap().replace( | ||
T::try_from(cldr_paths).map_err(|e| DataError::ResourceError(Box::new(e)))?, | ||
); | ||
}; | ||
self | ||
.data_provider | ||
.read() | ||
.unwrap() | ||
.as_ref() | ||
.unwrap() | ||
.load(req) | ||
.map(Some) | ||
} | ||
|
||
pub fn try_iter( | ||
&self, | ||
data_key: &DataKey, | ||
cldr_paths: &'b CldrPaths, | ||
) -> Result<Option<Box<dyn Iterator<Item = DataEntry>>>, DataError> { | ||
if T::supports_key(data_key).is_err() { | ||
return Ok(None); | ||
} | ||
if self.data_provider.read().unwrap().is_none() { | ||
self.data_provider.write().unwrap().replace( | ||
T::try_from(cldr_paths).map_err(|e| DataError::ResourceError(Box::new(e)))?, | ||
); | ||
}; | ||
self | ||
.data_provider | ||
.read() | ||
.unwrap() | ||
.as_ref() | ||
.unwrap() | ||
.iter_for_key(data_key) | ||
.map(Some) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
mod plurals; | ||
|
||
pub use plurals::PluralsProvider; | ||
|
||
use crate::support::LazyCldrProvider; | ||
use crate::CldrPaths; | ||
use icu_data_provider::iter::DataEntryCollection; | ||
use icu_data_provider::prelude::*; | ||
|
||
pub struct CldrJsonDataProvider<'a, 'd> { | ||
pub cldr_paths: &'a CldrPaths, | ||
plurals: LazyCldrProvider<PluralsProvider<'d>>, | ||
} | ||
|
||
impl<'a, 'd> CldrJsonDataProvider<'a, 'd> { | ||
pub fn new(cldr_paths: &'a CldrPaths) -> Self { | ||
CldrJsonDataProvider { | ||
cldr_paths, | ||
plurals: LazyCldrProvider::new(), | ||
} | ||
} | ||
} | ||
|
||
impl<'a, 'd> DataProvider<'d> for CldrJsonDataProvider<'a, 'd> { | ||
fn load(&self, req: &DataRequest) -> Result<DataResponse<'d>, DataError> { | ||
if let Some(resp) = self.plurals.try_load(req, &self.cldr_paths)? { | ||
return Ok(resp); | ||
} | ||
Err(DataError::UnsupportedDataKey(req.data_key)) | ||
} | ||
} | ||
|
||
impl<'a, 'd> DataEntryCollection for CldrJsonDataProvider<'a, 'd> { | ||
fn iter_for_key( | ||
&self, | ||
data_key: &DataKey, | ||
) -> Result<Box<dyn Iterator<Item = DataEntry>>, DataError> { | ||
if let Some(resp) = self.plurals.try_iter(data_key, &self.cldr_paths)? { | ||
return Ok(resp); | ||
} | ||
Err(DataError::UnsupportedDataKey(*data_key)) | ||
} | ||
} |
Oops, something went wrong.