Skip to content

Commit 27a2466

Browse files
committed
Merge branch 'main' into restructure
2 parents 0289bd4 + 3465386 commit 27a2466

File tree

27 files changed

+2981
-2440
lines changed

27 files changed

+2981
-2440
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ dhat-heap.json
2525
# Do not check-in binary file tree test data
2626
provider/testdata/data/bincode
2727
provider/testdata/data/postcard/*
28-
!provider/testdata/data/postcard/fingerprints.txt
28+
!provider/testdata/data/postcard/fingerprints.csv
2929

3030
# Ignore irrelevant files that get generated on macOS
3131
**/.DS_Store

Cargo.lock

Lines changed: 2 additions & 21 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ members = [
2626
"ffi/capi_cdylib",
2727
"ffi/diplomat",
2828
"ffi/capi_staticlib",
29-
"ffi/ecma402",
3029
"ffi/freertos",
3130
"provider/adapters",
3231
"provider/blob",
@@ -55,7 +54,11 @@ members = [
5554
"utils/zerovec",
5655
"utils/zerovec/derive",
5756
]
58-
exclude = ["provider/testdata/data/const"]
57+
exclude = [
58+
# This crate uses datagen which requires downloading ZIP files during build,
59+
# so we don't want to build it as part of the workspace.
60+
"ffi/ecma402",
61+
]
5962

6063
# LTO is needed for WASM and other size-optimized builds,
6164
# and it improve the performance of benchmarks

components/list/src/provider.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -262,14 +262,14 @@ mod datagen {
262262
pub fn make_conditional(
263263
&mut self,
264264
pattern: &str,
265-
regex: &str,
265+
regex: &StringMatcher<'static>,
266266
alternative_pattern: &str,
267267
) -> Result<(), DataError> {
268268
let old = ListJoinerPattern::from_str(pattern, true, true)?;
269269
for i in 0..12 {
270270
if self.0[i].default == old {
271271
self.0[i].special_case = Some(SpecialCasePattern {
272-
condition: StringMatcher::new(regex)?,
272+
condition: regex.clone(),
273273
pattern: ListJoinerPattern::from_str(
274274
alternative_pattern,
275275
i % 4 == 0 || i % 4 == 3, // allow_prefix = start or pair
@@ -363,7 +363,7 @@ pub(crate) mod test {
363363
])
364364
.unwrap();
365365
patterns
366-
.make_conditional("{0}. {1}", "A", "{0} :o {1}")
366+
.make_conditional("{0}. {1}", &StringMatcher::new("A").unwrap(), "{0} :o {1}")
367367
.unwrap();
368368
patterns
369369
}

ffi/ecma402/Cargo.toml

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,13 @@ all-features = true
2828

2929
[dependencies]
3030
ecma402_traits = { version = "0.2.0" }
31-
icu = { version = "0.6", path = "../../components/icu", default-features = false }
31+
icu = { version = "0.6", path = "../../components/icu", features = ["std"] }
3232
icu_provider = { version = "0.6", path = "../../provider/core" }
33-
icu_plurals = { version = "0.6", path = "../../components/plurals", features = ["std"] }
3433

34+
# Bake dependencies
35+
icu_plurals = { version = "0.6", path = "../../components/plurals" }
36+
zerovec = { version = "0.7", path = "../../utils/zerovec" }
3537

36-
[dev-dependencies]
37-
criterion = "0.3"
38-
serde = { version = "1.0", default-features = false, features = ["derive", "alloc"] }
39-
serde_json = {version = "1.0" }
40-
icu_locid = { version = "0.6", path = "../../components/locid" }
41-
icu_provider = { version = "0.6", path = "../../provider/core" }
42-
icu_testdata = { version = "0.6", path = "../../provider/testdata" }
38+
[build-dependencies]
39+
icu = { version = "0.6", path = "../../components/icu", features = ["std"] }
40+
icu_datagen = { version = "0.6", path = "../../provider/datagen" }

ffi/ecma402/build.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// This file is part of ICU4X. For terms of use, please see the file
2+
// called LICENSE at the top level of the ICU4X source tree
3+
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
4+
5+
fn main() {
6+
println!("cargo:rerun-if-changed=build.rs");
7+
8+
icu_datagen::datagen(
9+
None,
10+
&icu_datagen::keys(&["plurals/cardinal@1", "plurals/ordinal@1"]),
11+
&icu_datagen::SourceData::default()
12+
.with_cldr_latest(icu_datagen::CldrLocaleSubset::Full)
13+
.unwrap(),
14+
vec![icu_datagen::Out::Module {
15+
mod_directory: std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap())
16+
.join("baked"),
17+
pretty: false,
18+
insert_feature_gates: false,
19+
}],
20+
)
21+
.unwrap();
22+
}

ffi/ecma402/src/lib.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use icu::locid::LanguageIdentifier;
1919

2020
/// Implements ECMA-402 [`Intl.PluralRules`][link].
2121
///
22-
/// [link]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/PluralRulres/PluralRules
22+
/// [link]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/PluralRules
2323
pub mod pluralrules;
2424

2525
/// An adapter between [`icu::locid`] and [`ecma402_traits`].
@@ -46,3 +46,9 @@ impl std::fmt::Display for crate::Locale {
4646
}
4747
}
4848
}
49+
50+
mod provider {
51+
include!(concat!(env!("OUT_DIR"), "/baked/mod.rs"));
52+
}
53+
54+
pub(crate) use provider::BakedDataProvider;

ffi/ecma402/src/pluralrules.rs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -241,9 +241,7 @@ impl ecma402_traits::pluralrules::PluralRules for PluralRules {
241241
L: ecma402_traits::Locale,
242242
Self: Sized,
243243
{
244-
// TODO: introduce a global data provider here.
245-
let dp = icu_provider::inv::InvariantDataProvider;
246-
Self::try_new_with_provider(l, opts, &dp)
244+
Self::try_new_with_provider(l, opts, &crate::BakedDataProvider)
247245
}
248246

249247
fn select<W>(&self, number: f64, writer: &mut W) -> std::fmt::Result
@@ -330,13 +328,9 @@ mod testing {
330328
expected: vec!["other", "other", "other", "other", "other", "other"],
331329
},
332330
];
333-
let provider = icu_testdata::get_provider();
334331
for (i, test) in tests.into_iter().enumerate() {
335-
let plr = super::PluralRules::try_new_with_provider(
336-
crate::Locale::FromLocale(test.locale),
337-
test.opts,
338-
&provider,
339-
)?;
332+
let plr =
333+
super::PluralRules::try_new(crate::Locale::FromLocale(test.locale), test.opts)?;
340334
assert_eq!(
341335
test.numbers
342336
.iter()

provider/adapters/src/fallback/adapter.rs

Lines changed: 165 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,181 @@
33
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
44

55
use super::*;
6+
use crate::helpers::result_is_err_missing_resource_options;
67

7-
pub trait LocaleFallbackProvider {
8-
fn load_fallback_metadata_for_key(
8+
/// A data provider wrapper that performs locale fallback. This enables arbitrary locales to be
9+
/// handled at runtime.
10+
///
11+
/// # Examples
12+
///
13+
/// ```
14+
/// use icu_locid::locale;
15+
/// use icu_provider::prelude::*;
16+
/// use icu_provider::hello_world::*;
17+
/// use icu_provider_adapters::fallback::LocaleFallbackProvider;
18+
///
19+
/// let provider = icu_testdata::get_provider();
20+
///
21+
/// let req = DataRequest {
22+
/// options: locale!("ja-JP").into(),
23+
/// metadata: Default::default(),
24+
/// };
25+
///
26+
/// // The provider does not have data for "ja-JP":
27+
/// let result: Result<DataResponse<HelloWorldV1Marker>, DataError> =
28+
/// provider.load_resource(&req);
29+
/// assert!(matches!(result, Err(_)));
30+
///
31+
/// // But if we wrap the provider in a fallback provider...
32+
/// let provider = LocaleFallbackProvider::try_new(provider)
33+
/// .expect("Fallback data present");
34+
///
35+
/// // ...then we can load "ja-JP" based on "ja" data
36+
/// let result: Result<DataResponse<HelloWorldV1Marker>, DataError> =
37+
/// provider.load_resource(&req);
38+
/// assert!(matches!(result, Ok(_)));
39+
///
40+
/// assert_eq!(
41+
/// "こんにちは世界",
42+
/// result.unwrap().take_payload().unwrap().get().message
43+
/// );
44+
/// ```
45+
pub struct LocaleFallbackProvider<P> {
46+
pub inner: P,
47+
fallbacker: LocaleFallbacker,
48+
}
49+
50+
impl<P> LocaleFallbackProvider<P>
51+
where
52+
P: ResourceProvider<LocaleFallbackLikelySubtagsV1Marker>
53+
+ ResourceProvider<LocaleFallbackParentsV1Marker>,
54+
{
55+
/// Create a [`LocaleFallbackProvider`] by wrapping another data provider and then loading
56+
/// fallback data from it.
57+
///
58+
/// If the data provider being wrapped does not contain fallback data, use
59+
/// [`LocaleFallbackProvider::new_with_fallbacker`].
60+
pub fn try_new(provider: P) -> Result<Self, DataError> {
61+
let fallbacker = LocaleFallbacker::try_new(&provider)?;
62+
Ok(Self {
63+
inner: provider,
64+
fallbacker,
65+
})
66+
}
67+
}
68+
69+
impl<P> LocaleFallbackProvider<P> {
70+
/// Wrap a provider with an arbitrary fallback engine.
71+
///
72+
/// This relaxes the requirement that the wrapped provider contains its own fallback data.
73+
///
74+
/// # Examples
75+
///
76+
/// ```
77+
/// use icu_locid::locale;
78+
/// use icu_provider::hello_world::*;
79+
/// use icu_provider::prelude::*;
80+
/// use icu_provider_adapters::fallback::{LocaleFallbacker, LocaleFallbackProvider};
81+
///
82+
/// let provider = HelloWorldProvider;
83+
///
84+
/// let req = DataRequest {
85+
/// options: locale!("de-CH").into(),
86+
/// metadata: Default::default(),
87+
/// };
88+
///
89+
/// // There is no "de-CH" data in the `HelloWorldProvider`
90+
/// ResourceProvider::<HelloWorldV1Marker>::load_resource(&provider, &req)
91+
/// .expect_err("No data for de-CH");
92+
///
93+
/// // `HelloWorldProvider` does not contain fallback data,
94+
/// // but we can fetch it from `icu_testdata`, and then
95+
/// // use it to create the fallbacking data provider.
96+
/// let fallbacker = LocaleFallbacker::try_new(&icu_testdata::get_provider())
97+
/// .expect("Fallback data present");
98+
/// let provider = LocaleFallbackProvider::new_with_fallbacker(provider, fallbacker);
99+
///
100+
/// // Now we can load the "de-CH" data via fallback to "de".
101+
/// let german_hello_world: DataPayload<HelloWorldV1Marker> = provider
102+
/// .load_resource(&req)
103+
/// .expect("Loading should succeed")
104+
/// .take_payload()
105+
/// .expect("Data should be present");
106+
///
107+
/// assert_eq!("Hallo Welt", german_hello_world.get().message);
108+
/// ```
109+
pub fn new_with_fallbacker(provider: P, fallbacker: LocaleFallbacker) -> Self {
110+
Self {
111+
inner: provider,
112+
fallbacker,
113+
}
114+
}
115+
116+
fn run_fallback<F, R>(
9117
&self,
10118
key: ResourceKey,
11-
) -> Result<LocaleFallbackKeyMetadata, DataError>;
119+
base_req: &DataRequest,
120+
mut f: F,
121+
) -> Result<R, DataError>
122+
where
123+
F: FnMut(&DataRequest) -> Result<R, DataError>,
124+
{
125+
let key_fallbacker = self.fallbacker.for_key(key);
126+
let mut fallback_iterator = key_fallbacker.fallback_for(base_req.clone());
127+
while !fallback_iterator.get().options.is_empty() {
128+
let result = f(fallback_iterator.get());
129+
if !result_is_err_missing_resource_options(&result) {
130+
// Log the original request rather than the fallback request
131+
return result.map_err(|e| e.with_req(key, base_req));
132+
}
133+
fallback_iterator.step();
134+
}
135+
Err(DataErrorKind::MissingResourceOptions.with_req(key, base_req))
136+
}
12137
}
13138

14-
pub struct LocaleFallbackAdapter<P>(pub P);
139+
impl<P> AnyProvider for LocaleFallbackProvider<P>
140+
where
141+
P: AnyProvider,
142+
{
143+
fn load_any(&self, key: ResourceKey, base_req: &DataRequest) -> Result<AnyResponse, DataError> {
144+
self.run_fallback(key, base_req, |req| self.inner.load_any(key, req))
145+
}
146+
}
15147

16-
impl<P> BufferProvider for LocaleFallbackAdapter<P>
148+
impl<P> BufferProvider for LocaleFallbackProvider<P>
17149
where
18-
P: LocaleFallbackProvider + BufferProvider,
150+
P: BufferProvider,
19151
{
20152
fn load_buffer(
21153
&self,
22-
_key: ResourceKey,
23-
_req: &DataRequest,
154+
key: ResourceKey,
155+
base_req: &DataRequest,
24156
) -> Result<DataResponse<BufferMarker>, DataError> {
25-
todo!()
157+
self.run_fallback(key, base_req, |req| self.inner.load_buffer(key, req))
158+
}
159+
}
160+
161+
impl<P, M> DynProvider<M> for LocaleFallbackProvider<P>
162+
where
163+
P: DynProvider<M>,
164+
M: ResourceMarker,
165+
{
166+
fn load_payload(
167+
&self,
168+
key: ResourceKey,
169+
base_req: &DataRequest,
170+
) -> Result<DataResponse<M>, DataError> {
171+
self.run_fallback(key, base_req, |req| self.inner.load_payload(key, req))
172+
}
173+
}
174+
175+
impl<P, M> ResourceProvider<M> for LocaleFallbackProvider<P>
176+
where
177+
P: ResourceProvider<M>,
178+
M: ResourceMarker,
179+
{
180+
fn load_resource(&self, base_req: &DataRequest) -> Result<DataResponse<M>, DataError> {
181+
self.run_fallback(M::KEY, base_req, |req| self.inner.load_resource(req))
26182
}
27183
}

0 commit comments

Comments
 (0)