summaryrefslogtreecommitdiffstats
path: root/vendor/icu_provider_adapters
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:18:32 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:18:32 +0000
commit4547b622d8d29df964fa2914213088b148c498fc (patch)
tree9fc6b25f3c3add6b745be9a2400a6e96140046e9 /vendor/icu_provider_adapters
parentReleasing progress-linux version 1.66.0+dfsg1-1~progress7.99u1. (diff)
downloadrustc-4547b622d8d29df964fa2914213088b148c498fc.tar.xz
rustc-4547b622d8d29df964fa2914213088b148c498fc.zip
Merging upstream version 1.67.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/icu_provider_adapters')
-rw-r--r--vendor/icu_provider_adapters/.cargo-checksum.json1
-rw-r--r--vendor/icu_provider_adapters/Cargo.toml88
-rw-r--r--vendor/icu_provider_adapters/LICENSE51
-rw-r--r--vendor/icu_provider_adapters/README.md12
-rw-r--r--vendor/icu_provider_adapters/src/any_payload.rs123
-rw-r--r--vendor/icu_provider_adapters/src/either.rs111
-rw-r--r--vendor/icu_provider_adapters/src/empty.rs88
-rw-r--r--vendor/icu_provider_adapters/src/fallback/adapter.rs273
-rw-r--r--vendor/icu_provider_adapters/src/fallback/algorithms.rs419
-rw-r--r--vendor/icu_provider_adapters/src/fallback/mod.rs390
-rw-r--r--vendor/icu_provider_adapters/src/fallback/provider.rs111
-rw-r--r--vendor/icu_provider_adapters/src/filter/impls.rs215
-rw-r--r--vendor/icu_provider_adapters/src/filter/mod.rs242
-rw-r--r--vendor/icu_provider_adapters/src/fork/by_error.rs287
-rw-r--r--vendor/icu_provider_adapters/src/fork/macros.rs71
-rw-r--r--vendor/icu_provider_adapters/src/fork/mod.rs226
-rw-r--r--vendor/icu_provider_adapters/src/fork/predicates.rs138
-rw-r--r--vendor/icu_provider_adapters/src/helpers.rs15
-rw-r--r--vendor/icu_provider_adapters/src/lib.rs36
-rw-r--r--vendor/icu_provider_adapters/tests/data/langtest/de/core/helloworld@1/de.json1
-rw-r--r--vendor/icu_provider_adapters/tests/data/langtest/de/manifest.json3
-rw-r--r--vendor/icu_provider_adapters/tests/data/langtest/ro/core/helloworld@1/ro.json1
-rw-r--r--vendor/icu_provider_adapters/tests/data/langtest/ro/manifest.json3
23 files changed, 2905 insertions, 0 deletions
diff --git a/vendor/icu_provider_adapters/.cargo-checksum.json b/vendor/icu_provider_adapters/.cargo-checksum.json
new file mode 100644
index 000000000..82805760b
--- /dev/null
+++ b/vendor/icu_provider_adapters/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"Cargo.toml":"5ecb7cc7b9f229262dc3694db154ab479591430d0d10e03b5aed677067c16d09","LICENSE":"4ad7541d66a407234e2c84902124cef325c29f3e966353efdb800bedb8b8da21","README.md":"e6a08c2bc307b66c93de20c2c366da896f7fd3f5e5eb402edd2eeba79476b600","src/any_payload.rs":"55336b0a861be2da125a411924a3597de5593135c8ff589420237189b77311f8","src/either.rs":"aa309c26deafcb9470bb9eb15e629fc53f2fb5d7e412d1a2317bc170f0652588","src/empty.rs":"2ef71bb0eb3b81cf8cbb9f93c37bb0b7a106b4db941ffdefc8e88b944bee90d2","src/fallback/adapter.rs":"31d851e1d2adda59c06510cdfc43846a107ee706b731b3019b7c28571f0398ff","src/fallback/algorithms.rs":"bc8d17fb957f8f9ada8e13d6f0d35af248caa621441ffb3fcd273ff97e39e4a4","src/fallback/mod.rs":"ec165267f61c722300cc4a44be7561e31a5c65007f8a4c11fa074f31d768797a","src/fallback/provider.rs":"ef8a835e3ed2b7871b6f6e78ae2e73575b26adc9e8e0b5a7170c8513e7fe3269","src/filter/impls.rs":"58fcc1f03454769a3c11a92cb4b7b9f7833a0c042ea80b4e863609e2414eed0f","src/filter/mod.rs":"960e1c22d7d2323bf73c3ff6ea2d34158ff767f2a6b4fc8c40e982bf8c2dd500","src/fork/by_error.rs":"67742b78a80ff9765fd83fc28390687ed6e07f71afc0964b20221ff1f508e380","src/fork/macros.rs":"5aebb0134923fa9fa0fe7c16d2d85a1a29cf4ea49505187db56bf0bc8e984ad0","src/fork/mod.rs":"7378895128bf1ccc9de7f5675668ef922d3df7b84b04f68af4d5b49efe60341e","src/fork/predicates.rs":"314bf33144c6827cd3df05877d7c20fb64c9b18f6230e229340b8f04f4ccf4d3","src/helpers.rs":"008af3aa36ebf43ec249dd9162ff36c150fa00fe78b3065b023d59acbe218b7b","src/lib.rs":"7499b4bcb40453bc4678d6ae3febf05f5c1e351120513ddd694983e6da56f268","tests/data/langtest/de/core/helloworld@1/de.json":"5a45b1d80567de8c4ff754f7dbe20c22477c1e00a9400b38397eb034f295b8f8","tests/data/langtest/de/manifest.json":"fa2f848cff051fd12a909389fbbc44b93ae1feb92cce466cc4381f9548443ea9","tests/data/langtest/ro/core/helloworld@1/ro.json":"b6b68292746dd6bb2d92d9c08e2753db556ec9c954a3b593b0b8999df550f298","tests/data/langtest/ro/manifest.json":"fa2f848cff051fd12a909389fbbc44b93ae1feb92cce466cc4381f9548443ea9"},"package":"980c71d8a91b246ebbb97847178a4b816eea39d1d550c70ee566384555bb6545"} \ No newline at end of file
diff --git a/vendor/icu_provider_adapters/Cargo.toml b/vendor/icu_provider_adapters/Cargo.toml
new file mode 100644
index 000000000..365a2a69b
--- /dev/null
+++ b/vendor/icu_provider_adapters/Cargo.toml
@@ -0,0 +1,88 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2018"
+name = "icu_provider_adapters"
+version = "1.0.0"
+authors = ["The ICU4X Project Developers"]
+include = [
+ "src/**/*",
+ "examples/**/*",
+ "benches/**/*",
+ "tests/**/*",
+ "Cargo.toml",
+ "LICENSE",
+ "README.md",
+]
+description = "Adapters for composing and manipulating data providers."
+readme = "README.md"
+categories = ["internationalization"]
+license = "Unicode-DFS-2016"
+repository = "https://github.com/unicode-org/icu4x"
+resolver = "2"
+
+[dependencies.databake]
+version = "0.1.0"
+features = ["derive"]
+optional = true
+
+[dependencies.icu_locid]
+version = "1.0.0"
+features = ["zerovec"]
+
+[dependencies.icu_provider]
+version = "1.0.0"
+features = ["macros"]
+
+[dependencies.serde]
+version = "1.0"
+features = [
+ "derive",
+ "alloc",
+]
+optional = true
+default-features = false
+
+[dependencies.tinystr]
+version = "0.7"
+features = ["zerovec"]
+
+[dependencies.yoke]
+version = "0.6"
+
+[dependencies.zerovec]
+version = "0.9"
+features = ["yoke"]
+
+[dev-dependencies.icu_provider]
+version = "1.0.0"
+features = [
+ "macros",
+ "deserialize_json",
+]
+
+[features]
+datagen = [
+ "std",
+ "serde",
+ "databake",
+ "icu_provider/datagen",
+ "icu_locid/databake",
+ "zerovec/databake",
+]
+serde = [
+ "dep:serde",
+ "zerovec/serde",
+ "icu_locid/serde",
+ "icu_provider/serde",
+]
+std = ["icu_locid/std"]
diff --git a/vendor/icu_provider_adapters/LICENSE b/vendor/icu_provider_adapters/LICENSE
new file mode 100644
index 000000000..9858d01ab
--- /dev/null
+++ b/vendor/icu_provider_adapters/LICENSE
@@ -0,0 +1,51 @@
+UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE
+
+See Terms of Use <https://www.unicode.org/copyright.html>
+for definitions of Unicode Inc.’s Data Files and Software.
+
+NOTICE TO USER: Carefully read the following legal agreement.
+BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S
+DATA FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"),
+YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE
+TERMS AND CONDITIONS OF THIS AGREEMENT.
+IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE
+THE DATA FILES OR SOFTWARE.
+
+COPYRIGHT AND PERMISSION NOTICE
+
+Copyright © 1991-2022 Unicode, Inc. All rights reserved.
+Distributed under the Terms of Use in https://www.unicode.org/copyright.html.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Unicode data files and any associated documentation
+(the "Data Files") or Unicode software and any associated documentation
+(the "Software") to deal in the Data Files or Software
+without restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, and/or sell copies of
+the Data Files or Software, and to permit persons to whom the Data Files
+or Software are furnished to do so, provided that either
+(a) this copyright and permission notice appear with all copies
+of the Data Files or Software, or
+(b) this copyright and permission notice appear in associated
+Documentation.
+
+THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS
+NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL
+DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THE DATA FILES OR SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder
+shall not be used in advertising or otherwise to promote the sale,
+use or other dealings in these Data Files or Software without prior
+written authorization of the copyright holder.
+
+—
+
+Portions of ICU4X may have been adapted from ICU4C and/or ICU4J.
+ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others.
diff --git a/vendor/icu_provider_adapters/README.md b/vendor/icu_provider_adapters/README.md
new file mode 100644
index 000000000..28a29e6bc
--- /dev/null
+++ b/vendor/icu_provider_adapters/README.md
@@ -0,0 +1,12 @@
+# icu_provider_adapters [![crates.io](https://img.shields.io/crates/v/icu_provider_adapters)](https://crates.io/crates/icu_provider_adapters)
+
+Adapters for composing and manipulating data providers.
+
+- Use the [`fork`] module to marshall data requests between multiple possible providers.
+- Use the [`either`] module to choose between multiple provider types at runtime.
+- Use the [`filter`] module to programmatically reject certain data requests.
+- Use the [`fallback`] module to automatically resolve arbitrary locales for data loading.
+
+## More Information
+
+For more information on development, authorship, contributing etc. please visit [`ICU4X home page`](https://github.com/unicode-org/icu4x).
diff --git a/vendor/icu_provider_adapters/src/any_payload.rs b/vendor/icu_provider_adapters/src/any_payload.rs
new file mode 100644
index 000000000..bbb9220c3
--- /dev/null
+++ b/vendor/icu_provider_adapters/src/any_payload.rs
@@ -0,0 +1,123 @@
+// 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 ).
+
+//! Data provider always serving the same struct.
+
+use icu_provider::prelude::*;
+use yoke::trait_hack::YokeTraitHack;
+use yoke::Yokeable;
+use zerofrom::ZeroFrom;
+
+/// A data provider that returns clones of a fixed type-erased payload.
+///
+/// [`AnyPayloadProvider`] implements [`AnyProvider`], so it can be used in
+/// `*_with_any_provider` constructors across ICU4X.
+///
+/// # Examples
+///
+/// ```
+/// use icu_provider::hello_world::*;
+/// use icu_provider::prelude::*;
+/// use icu_provider_adapters::any_payload::AnyPayloadProvider;
+/// use std::borrow::Cow;
+/// use writeable::assert_writeable_eq;
+///
+/// let provider =
+/// AnyPayloadProvider::from_static::<HelloWorldV1Marker>(&HelloWorldV1 {
+/// message: Cow::Borrowed("custom hello world"),
+/// });
+///
+/// // Check that it works:
+/// let formatter = HelloWorldFormatter::try_new_with_any_provider(
+/// &provider,
+/// &icu_locid::Locale::UND.into(),
+/// )
+/// .expect("key matches");
+/// assert_writeable_eq!(formatter.format(), "custom hello world");
+///
+/// // Requests for invalid keys get MissingDataKey
+/// assert!(matches!(
+/// provider.load_any(icu_provider::data_key!("foo@1"), Default::default()),
+/// Err(DataError {
+/// kind: DataErrorKind::MissingDataKey,
+/// ..
+/// })
+/// ))
+/// ```
+#[allow(clippy::exhaustive_structs)] // this type is stable
+pub struct AnyPayloadProvider {
+ /// The [`DataKey`] for which to provide data. All others will receive a
+ /// [`DataErrorKind::MissingDataKey`].
+ key: DataKey,
+ /// The [`AnyPayload`] to return on matching requests.
+ data: AnyPayload,
+}
+
+impl AnyPayloadProvider {
+ /// Creates an `AnyPayloadProvider` with an owned (allocated) payload of the given data.
+ pub fn from_owned<M: KeyedDataMarker + 'static>(data: M::Yokeable) -> Self
+ where
+ M::Yokeable: icu_provider::MaybeSendSync,
+ {
+ Self::from_payload::<M>(DataPayload::from_owned(data))
+ }
+
+ /// Creates an `AnyPayloadProvider` with a statically borrowed payload of the given data.
+ pub fn from_static<M: KeyedDataMarker>(data: &'static M::Yokeable) -> Self {
+ AnyPayloadProvider {
+ key: M::KEY,
+ data: AnyPayload::from_static_ref(data),
+ }
+ }
+
+ /// Creates an `AnyPayloadProvider` from an existing [`DataPayload`].
+ pub fn from_payload<M: KeyedDataMarker + 'static>(payload: DataPayload<M>) -> Self
+ where
+ M::Yokeable: icu_provider::MaybeSendSync,
+ {
+ AnyPayloadProvider {
+ key: M::KEY,
+ data: payload.wrap_into_any_payload(),
+ }
+ }
+
+ /// Creates an `AnyPayloadProvider` from an existing [`AnyPayload`].
+ pub fn from_any_payload<M: KeyedDataMarker + 'static>(payload: AnyPayload) -> Self {
+ AnyPayloadProvider {
+ key: M::KEY,
+ data: payload,
+ }
+ }
+
+ /// Creates an `AnyPayloadProvider` with the default (allocated) version of the data struct.
+ pub fn new_default<M: KeyedDataMarker + 'static>() -> Self
+ where
+ M::Yokeable: Default,
+ M::Yokeable: icu_provider::MaybeSendSync,
+ {
+ Self::from_owned::<M>(M::Yokeable::default())
+ }
+}
+
+impl AnyProvider for AnyPayloadProvider {
+ fn load_any(&self, key: DataKey, _: DataRequest) -> Result<AnyResponse, DataError> {
+ key.match_key(self.key)?;
+ Ok(AnyResponse {
+ metadata: DataResponseMetadata::default(),
+ payload: Some(self.data.clone()),
+ })
+ }
+}
+
+impl<M> DataProvider<M> for AnyPayloadProvider
+where
+ M: KeyedDataMarker + 'static,
+ for<'a> YokeTraitHack<<M::Yokeable as Yokeable<'a>>::Output>: Clone,
+ M::Yokeable: ZeroFrom<'static, M::Yokeable>,
+ M::Yokeable: icu_provider::MaybeSendSync,
+{
+ fn load(&self, req: DataRequest) -> Result<DataResponse<M>, DataError> {
+ self.as_downcasting().load(req)
+ }
+}
diff --git a/vendor/icu_provider_adapters/src/either.rs b/vendor/icu_provider_adapters/src/either.rs
new file mode 100644
index 000000000..b62a9c3fb
--- /dev/null
+++ b/vendor/icu_provider_adapters/src/either.rs
@@ -0,0 +1,111 @@
+// 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 ).
+
+//! Helpers for switching between multiple providers.
+
+use icu_provider::prelude::*;
+
+#[cfg(feature = "datagen")]
+use icu_provider::datagen;
+
+/// A provider that is one of two types determined at runtime.
+///
+/// Data provider traits implemented by both `P0` and `P1` are implemented on
+/// `EitherProvider<P0, P1>`.
+#[allow(clippy::exhaustive_enums)] // this is stable
+pub enum EitherProvider<P0, P1> {
+ /// A value of type `P0`.
+ A(P0),
+ /// A value of type `P1`.
+ B(P1),
+}
+
+impl<P0: AnyProvider, P1: AnyProvider> AnyProvider for EitherProvider<P0, P1> {
+ #[inline]
+ fn load_any(&self, key: DataKey, req: DataRequest) -> Result<AnyResponse, DataError> {
+ use EitherProvider::*;
+ match self {
+ A(p) => p.load_any(key, req),
+ B(p) => p.load_any(key, req),
+ }
+ }
+}
+
+impl<P0: BufferProvider, P1: BufferProvider> BufferProvider for EitherProvider<P0, P1> {
+ #[inline]
+ fn load_buffer(
+ &self,
+ key: DataKey,
+ req: DataRequest,
+ ) -> Result<DataResponse<BufferMarker>, DataError> {
+ use EitherProvider::*;
+ match self {
+ A(p) => p.load_buffer(key, req),
+ B(p) => p.load_buffer(key, req),
+ }
+ }
+}
+
+impl<M: DataMarker, P0: DynamicDataProvider<M>, P1: DynamicDataProvider<M>> DynamicDataProvider<M>
+ for EitherProvider<P0, P1>
+{
+ #[inline]
+ fn load_data(&self, key: DataKey, req: DataRequest) -> Result<DataResponse<M>, DataError> {
+ use EitherProvider::*;
+ match self {
+ A(p) => p.load_data(key, req),
+ B(p) => p.load_data(key, req),
+ }
+ }
+}
+
+impl<M: KeyedDataMarker, P0: DataProvider<M>, P1: DataProvider<M>> DataProvider<M>
+ for EitherProvider<P0, P1>
+{
+ #[inline]
+ fn load(&self, req: DataRequest) -> Result<DataResponse<M>, DataError> {
+ use EitherProvider::*;
+ match self {
+ A(p) => p.load(req),
+ B(p) => p.load(req),
+ }
+ }
+}
+
+#[cfg(feature = "datagen")]
+impl<
+ M: DataMarker,
+ P0: datagen::IterableDynamicDataProvider<M>,
+ P1: datagen::IterableDynamicDataProvider<M>,
+ > datagen::IterableDynamicDataProvider<M> for EitherProvider<P0, P1>
+{
+ #[inline]
+ fn supported_locales_for_key(
+ &self,
+ key: DataKey,
+ ) -> Result<alloc::vec::Vec<DataLocale>, DataError> {
+ use EitherProvider::*;
+ match self {
+ A(p) => p.supported_locales_for_key(key),
+ B(p) => p.supported_locales_for_key(key),
+ }
+ }
+}
+
+#[cfg(feature = "datagen")]
+impl<
+ M: KeyedDataMarker,
+ P0: datagen::IterableDataProvider<M>,
+ P1: datagen::IterableDataProvider<M>,
+ > datagen::IterableDataProvider<M> for EitherProvider<P0, P1>
+{
+ #[inline]
+ fn supported_locales(&self) -> Result<alloc::vec::Vec<DataLocale>, DataError> {
+ use EitherProvider::*;
+ match self {
+ A(p) => p.supported_locales(),
+ B(p) => p.supported_locales(),
+ }
+ }
+}
diff --git a/vendor/icu_provider_adapters/src/empty.rs b/vendor/icu_provider_adapters/src/empty.rs
new file mode 100644
index 000000000..31a6c42aa
--- /dev/null
+++ b/vendor/icu_provider_adapters/src/empty.rs
@@ -0,0 +1,88 @@
+// 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 ).
+
+//! Empty data provider implementations.
+//!
+//! Use [`EmptyDataProvider`] as a stand-in for a provider that always fails.
+
+use icu_provider::prelude::*;
+
+/// A data provider that always returns an error.
+///
+/// The returned error kind is configurable.
+///
+/// # Examples
+///
+/// ```
+/// use icu_provider::hello_world::HelloWorldV1Marker;
+/// use icu_provider::prelude::*;
+/// use icu_provider_adapters::empty::EmptyDataProvider;
+///
+/// let provider = EmptyDataProvider::new();
+///
+/// assert!(matches!(
+/// provider.load_any(HelloWorldV1Marker::KEY, Default::default()),
+/// Err(DataError {
+/// kind: DataErrorKind::MissingDataKey,
+/// ..
+/// })
+/// ));
+/// ```
+pub struct EmptyDataProvider {
+ error_kind: DataErrorKind,
+}
+
+impl Default for EmptyDataProvider {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl EmptyDataProvider {
+ /// Creates a data provider that always returns [`DataErrorKind::MissingDataKey`].
+ pub fn new() -> Self {
+ Self {
+ error_kind: DataErrorKind::MissingDataKey,
+ }
+ }
+
+ /// Creates a data provider that always returns the specified error kind.
+ pub fn new_with_error_kind(error_kind: DataErrorKind) -> Self {
+ Self { error_kind }
+ }
+}
+
+impl AnyProvider for EmptyDataProvider {
+ fn load_any(&self, key: DataKey, base_req: DataRequest) -> Result<AnyResponse, DataError> {
+ Err(self.error_kind.with_req(key, base_req))
+ }
+}
+
+impl BufferProvider for EmptyDataProvider {
+ fn load_buffer(
+ &self,
+ key: DataKey,
+ base_req: DataRequest,
+ ) -> Result<DataResponse<BufferMarker>, DataError> {
+ Err(self.error_kind.with_req(key, base_req))
+ }
+}
+
+impl<M> DynamicDataProvider<M> for EmptyDataProvider
+where
+ M: DataMarker,
+{
+ fn load_data(&self, key: DataKey, base_req: DataRequest) -> Result<DataResponse<M>, DataError> {
+ Err(self.error_kind.with_req(key, base_req))
+ }
+}
+
+impl<M> DataProvider<M> for EmptyDataProvider
+where
+ M: KeyedDataMarker,
+{
+ fn load(&self, base_req: DataRequest) -> Result<DataResponse<M>, DataError> {
+ Err(self.error_kind.with_req(M::KEY, base_req))
+ }
+}
diff --git a/vendor/icu_provider_adapters/src/fallback/adapter.rs b/vendor/icu_provider_adapters/src/fallback/adapter.rs
new file mode 100644
index 000000000..4d1f79255
--- /dev/null
+++ b/vendor/icu_provider_adapters/src/fallback/adapter.rs
@@ -0,0 +1,273 @@
+// 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 super::*;
+use crate::helpers::result_is_err_missing_data_options;
+
+/// A data provider wrapper that performs locale fallback. This enables arbitrary locales to be
+/// handled at runtime.
+///
+/// # Examples
+///
+/// ```
+/// use icu_locid::locale;
+/// use icu_provider::prelude::*;
+/// use icu_provider::hello_world::*;
+/// use icu_provider_adapters::fallback::LocaleFallbackProvider;
+///
+/// let provider = icu_testdata::unstable_no_fallback();
+///
+/// let req = DataRequest {
+/// locale: &locale!("ja-JP").into(),
+/// metadata: Default::default(),
+/// };
+///
+/// // The provider does not have data for "ja-JP":
+/// DataProvider::<HelloWorldV1Marker>::load(&provider, req).expect_err("No fallback");
+///
+/// // But if we wrap the provider in a fallback provider...
+/// let provider = LocaleFallbackProvider::try_new_unstable(provider)
+/// .expect("Fallback data present");
+///
+/// // ...then we can load "ja-JP" based on "ja" data
+/// let response =
+/// DataProvider::<HelloWorldV1Marker>::load(&provider, req).expect("successful with vertical fallback");
+///
+/// assert_eq!(
+/// "ja",
+/// response.metadata.locale.unwrap().to_string()
+/// );
+/// assert_eq!(
+/// "こんにちは世界",
+/// response.payload.unwrap().get().message
+/// );
+/// ```
+pub struct LocaleFallbackProvider<P> {
+ inner: P,
+ fallbacker: LocaleFallbacker,
+}
+
+impl<P> LocaleFallbackProvider<P>
+where
+ P: DataProvider<LocaleFallbackLikelySubtagsV1Marker>
+ + DataProvider<LocaleFallbackParentsV1Marker>
+ + DataProvider<CollationFallbackSupplementV1Marker>,
+{
+ /// Create a [`LocaleFallbackProvider`] by wrapping another data provider and then loading
+ /// fallback data from it.
+ ///
+ /// If the data provider being wrapped does not contain fallback data, use
+ /// [`LocaleFallbackProvider::new_with_fallbacker`].
+ pub fn try_new_unstable(provider: P) -> Result<Self, DataError> {
+ let fallbacker = LocaleFallbacker::try_new_unstable(&provider)?;
+ Ok(Self {
+ inner: provider,
+ fallbacker,
+ })
+ }
+}
+
+impl<P> LocaleFallbackProvider<P>
+where
+ P: AnyProvider,
+{
+ /// Create a [`LocaleFallbackProvider`] by wrapping another data provider and then loading
+ /// fallback data from it.
+ ///
+ /// If the data provider being wrapped does not contain fallback data, use
+ /// [`LocaleFallbackProvider::new_with_fallbacker`].
+ pub fn try_new_with_any_provider(provider: P) -> Result<Self, DataError> {
+ let fallbacker = LocaleFallbacker::try_new_with_any_provider(&provider)?;
+ Ok(Self {
+ inner: provider,
+ fallbacker,
+ })
+ }
+}
+
+#[cfg(feature = "serde")]
+impl<P> LocaleFallbackProvider<P>
+where
+ P: BufferProvider,
+{
+ /// Create a [`LocaleFallbackProvider`] by wrapping another data provider and then loading
+ /// fallback data from it.
+ ///
+ /// If the data provider being wrapped does not contain fallback data, use
+ /// [`LocaleFallbackProvider::new_with_fallbacker`].
+ pub fn try_new_with_buffer_provider(provider: P) -> Result<Self, DataError> {
+ let fallbacker = LocaleFallbacker::try_new_with_buffer_provider(&provider)?;
+ Ok(Self {
+ inner: provider,
+ fallbacker,
+ })
+ }
+}
+
+impl<P> LocaleFallbackProvider<P> {
+ /// Wrap a provider with an arbitrary fallback engine.
+ ///
+ /// This relaxes the requirement that the wrapped provider contains its own fallback data.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use icu_locid::locale;
+ /// use icu_provider::hello_world::*;
+ /// use icu_provider::prelude::*;
+ /// use icu_provider_adapters::fallback::{
+ /// LocaleFallbackProvider, LocaleFallbacker,
+ /// };
+ ///
+ /// let provider = HelloWorldProvider;
+ ///
+ /// let req = DataRequest {
+ /// locale: &locale!("de-CH").into(),
+ /// metadata: Default::default(),
+ /// };
+ ///
+ /// // There is no "de-CH" data in the `HelloWorldProvider`
+ /// DataProvider::<HelloWorldV1Marker>::load(&provider, req)
+ /// .expect_err("No data for de-CH");
+ ///
+ /// // `HelloWorldProvider` does not contain fallback data,
+ /// // but we can fetch it from `icu_testdata`, and then
+ /// // use it to create the fallbacking data provider.
+ /// let fallbacker =
+ /// LocaleFallbacker::try_new_unstable(&icu_testdata::unstable())
+ /// .expect("Fallback data present");
+ /// let provider =
+ /// LocaleFallbackProvider::new_with_fallbacker(provider, fallbacker);
+ ///
+ /// // Now we can load the "de-CH" data via fallback to "de".
+ /// let german_hello_world: DataPayload<HelloWorldV1Marker> = provider
+ /// .load(req)
+ /// .expect("Loading should succeed")
+ /// .take_payload()
+ /// .expect("Data should be present");
+ ///
+ /// assert_eq!("Hallo Welt", german_hello_world.get().message);
+ /// ```
+ pub fn new_with_fallbacker(provider: P, fallbacker: LocaleFallbacker) -> Self {
+ Self {
+ inner: provider,
+ fallbacker,
+ }
+ }
+
+ /// Returns a reference to the inner provider, bypassing fallback.
+ pub fn inner(&self) -> &P {
+ &self.inner
+ }
+
+ /// Returns ownership of the inner provider to the caller.
+ pub fn into_inner(self) -> P {
+ self.inner
+ }
+
+ /// Run the fallback algorithm with the data request using the inner data provider.
+ /// Internal function; external clients should use one of the trait impls below.
+ ///
+ /// Function arguments:
+ ///
+ /// - F1 should perform a data load for a single DataRequest and return the result of it
+ /// - F2 should map from the provider-specific response type to DataResponseMetadata
+ fn run_fallback<F1, F2, R>(
+ &self,
+ key: DataKey,
+ base_req: DataRequest,
+ mut f1: F1,
+ mut f2: F2,
+ ) -> Result<R, DataError>
+ where
+ F1: FnMut(DataRequest) -> Result<R, DataError>,
+ F2: FnMut(&mut R) -> &mut DataResponseMetadata,
+ {
+ let key_fallbacker = self.fallbacker.for_key(key);
+ let mut fallback_iterator = key_fallbacker.fallback_for(base_req.locale.clone());
+ loop {
+ let result = f1(DataRequest {
+ locale: fallback_iterator.get(),
+ metadata: Default::default(),
+ });
+ if !result_is_err_missing_data_options(&result) {
+ return result
+ .map(|mut res| {
+ f2(&mut res).locale = Some(fallback_iterator.take());
+ res
+ })
+ // Log the original request rather than the fallback request
+ .map_err(|e| e.with_req(key, base_req));
+ }
+ // If we just checked und, break out of the loop.
+ if fallback_iterator.get().is_empty() {
+ break;
+ }
+ fallback_iterator.step();
+ }
+ Err(DataErrorKind::MissingLocale.with_req(key, base_req))
+ }
+}
+
+impl<P> AnyProvider for LocaleFallbackProvider<P>
+where
+ P: AnyProvider,
+{
+ fn load_any(&self, key: DataKey, base_req: DataRequest) -> Result<AnyResponse, DataError> {
+ self.run_fallback(
+ key,
+ base_req,
+ |req| self.inner.load_any(key, req),
+ |res| &mut res.metadata,
+ )
+ }
+}
+
+impl<P> BufferProvider for LocaleFallbackProvider<P>
+where
+ P: BufferProvider,
+{
+ fn load_buffer(
+ &self,
+ key: DataKey,
+ base_req: DataRequest,
+ ) -> Result<DataResponse<BufferMarker>, DataError> {
+ self.run_fallback(
+ key,
+ base_req,
+ |req| self.inner.load_buffer(key, req),
+ |res| &mut res.metadata,
+ )
+ }
+}
+
+impl<P, M> DynamicDataProvider<M> for LocaleFallbackProvider<P>
+where
+ P: DynamicDataProvider<M>,
+ M: DataMarker,
+{
+ fn load_data(&self, key: DataKey, base_req: DataRequest) -> Result<DataResponse<M>, DataError> {
+ self.run_fallback(
+ key,
+ base_req,
+ |req| self.inner.load_data(key, req),
+ |res| &mut res.metadata,
+ )
+ }
+}
+
+impl<P, M> DataProvider<M> for LocaleFallbackProvider<P>
+where
+ P: DataProvider<M>,
+ M: KeyedDataMarker,
+{
+ fn load(&self, base_req: DataRequest) -> Result<DataResponse<M>, DataError> {
+ self.run_fallback(
+ M::KEY,
+ base_req,
+ |req| self.inner.load(req),
+ |res| &mut res.metadata,
+ )
+ }
+}
diff --git a/vendor/icu_provider_adapters/src/fallback/algorithms.rs b/vendor/icu_provider_adapters/src/fallback/algorithms.rs
new file mode 100644
index 000000000..9af52ef6d
--- /dev/null
+++ b/vendor/icu_provider_adapters/src/fallback/algorithms.rs
@@ -0,0 +1,419 @@
+// 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 icu_locid::extensions::unicode::Key;
+use icu_locid::extensions_unicode_key as key;
+use icu_locid::subtags::Language;
+use icu_locid::LanguageIdentifier;
+use icu_provider::FallbackPriority;
+
+use super::*;
+
+const SUBDIVISION_KEY: Key = key!("sd");
+
+impl<'a> LocaleFallbackerWithConfig<'a> {
+ pub(crate) fn normalize(&self, locale: &mut DataLocale) {
+ let language = locale.language();
+ // 1. Populate the region (required for region fallback only)
+ if self.config.priority == FallbackPriority::Region && locale.region().is_none() {
+ // 1a. First look for region based on language+script
+ if let Some(script) = locale.script() {
+ locale.set_region(
+ self.likely_subtags
+ .ls2r
+ .get_2d(&language.into(), &script.into())
+ .copied(),
+ );
+ }
+ // 1b. If that fails, try language only
+ if locale.region().is_none() {
+ locale.set_region(self.likely_subtags.l2r.get(&language.into()).copied());
+ }
+ }
+ // 2. Remove the script if it is implied by the other subtags
+ if let Some(script) = locale.script() {
+ let default_script = self
+ .likely_subtags
+ .l2s
+ .get_copied(&language.into())
+ .unwrap_or(DEFAULT_SCRIPT);
+ if let Some(region) = locale.region() {
+ if script
+ == self
+ .likely_subtags
+ .lr2s
+ .get_copied_2d(&language.into(), &region.into())
+ .unwrap_or(default_script)
+ {
+ locale.set_script(None);
+ }
+ } else if script == default_script {
+ locale.set_script(None);
+ }
+ }
+ // 3. Remove irrelevant extension subtags
+ locale.retain_unicode_ext(|key| {
+ match *key {
+ // Always retain -u-sd
+ SUBDIVISION_KEY => true,
+ // Retain the query-specific keyword
+ _ if Some(*key) == self.config.extension_key => true,
+ // Drop all others
+ _ => false,
+ }
+ });
+ // 4. If there is an invalid "sd" subtag, drop it
+ // For now, ignore it, and let fallback do it for us
+ }
+}
+
+impl<'a, 'b> LocaleFallbackIteratorInner<'a, 'b> {
+ pub fn step(&mut self, locale: &mut DataLocale) {
+ match self.config.priority {
+ FallbackPriority::Language => self.step_language(locale),
+ FallbackPriority::Region => self.step_region(locale),
+ // TODO(#1964): Change the collation fallback rules to be different
+ // from the language fallback fules.
+ FallbackPriority::Collation => self.step_language(locale),
+ // This case should not normally happen, but `FallbackPriority` is non_exhaustive.
+ // Make it go directly to `und`.
+ _ => {
+ debug_assert!(
+ false,
+ "Unknown FallbackPriority: {:?}",
+ self.config.priority
+ );
+ *locale = Default::default()
+ }
+ }
+ }
+
+ fn step_language(&mut self, locale: &mut DataLocale) {
+ // 1. Remove the extension fallback keyword
+ if let Some(extension_key) = self.config.extension_key {
+ if let Some(value) = locale.remove_unicode_ext(&extension_key) {
+ self.backup_extension = Some(value);
+ return;
+ }
+ }
+ // 2. Remove the subdivision keyword
+ if let Some(value) = locale.remove_unicode_ext(&SUBDIVISION_KEY) {
+ self.backup_subdivision = Some(value);
+ return;
+ }
+ // 3. Assert that the locale is a language identifier
+ debug_assert!(!locale.has_unicode_ext());
+ // 4. Remove variants
+ if locale.has_variants() {
+ self.backup_variants = Some(locale.clear_variants());
+ return;
+ }
+ // 5. Check for parent override
+ if let Some(parent) = self.get_explicit_parent(locale) {
+ locale.set_langid(parent);
+ self.restore_extensions_variants(locale);
+ return;
+ }
+ // 6. Add the script subtag if necessary
+ if locale.script().is_none() {
+ if let Some(region) = locale.region() {
+ let language = locale.language();
+ if let Some(script) = self
+ .likely_subtags
+ .lr2s
+ .get_copied_2d(&language.into(), &region.into())
+ {
+ locale.set_script(Some(script));
+ self.restore_extensions_variants(locale);
+ return;
+ }
+ }
+ }
+ // 7. Remove region
+ if locale.region().is_some() {
+ locale.set_region(None);
+ return;
+ }
+ // 8. Remove language+script
+ debug_assert!(!locale.language().is_empty()); // don't call .step() on und
+ locale.set_script(None);
+ locale.set_language(Language::UND);
+ }
+
+ fn step_region(&mut self, locale: &mut DataLocale) {
+ // 1. Remove the extension fallback keyword
+ if let Some(extension_key) = self.config.extension_key {
+ if let Some(value) = locale.remove_unicode_ext(&extension_key) {
+ self.backup_extension = Some(value);
+ return;
+ }
+ }
+ // 2. Remove the subdivision keyword
+ if let Some(value) = locale.remove_unicode_ext(&SUBDIVISION_KEY) {
+ self.backup_subdivision = Some(value);
+ return;
+ }
+ // 3. Assert that the locale is a language identifier
+ debug_assert!(!locale.has_unicode_ext());
+ // 4. Remove variants
+ if locale.has_variants() {
+ self.backup_variants = Some(locale.clear_variants());
+ return;
+ }
+ // 5. Remove language+script
+ if !locale.language().is_empty() || locale.script().is_some() {
+ locale.set_script(None);
+ locale.set_language(Language::UND);
+ self.restore_extensions_variants(locale);
+ return;
+ }
+ // 6. Remove region
+ debug_assert!(locale.region().is_some()); // don't call .step() on und
+ locale.set_region(None);
+ }
+
+ fn restore_extensions_variants(&mut self, locale: &mut DataLocale) {
+ if let Some(value) = self.backup_extension.take() {
+ #[allow(clippy::unwrap_used)] // not reachable unless extension_key is present
+ locale.set_unicode_ext(self.config.extension_key.unwrap(), value);
+ }
+ if let Some(value) = self.backup_subdivision.take() {
+ locale.set_unicode_ext(SUBDIVISION_KEY, value);
+ }
+ if let Some(variants) = self.backup_variants.take() {
+ locale.set_variants(variants);
+ }
+ }
+
+ fn get_explicit_parent(&self, locale: &DataLocale) -> Option<LanguageIdentifier> {
+ self.supplement
+ .and_then(|supplement| {
+ supplement
+ .parents
+ .get_copied_by(|uvstr| locale.strict_cmp(uvstr).reverse())
+ })
+ .or_else(|| {
+ self.parents
+ .parents
+ .get_copied_by(|uvstr| locale.strict_cmp(uvstr).reverse())
+ })
+ .map(LanguageIdentifier::from)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use icu_locid::Locale;
+ use std::str::FromStr;
+
+ struct TestCase {
+ input: &'static str,
+ requires_data: bool,
+ extension_key: Option<Key>,
+ fallback_supplement: Option<FallbackSupplement>,
+ // Note: The first entry in the chain is the normalized locale
+ expected_language_chain: &'static [&'static str],
+ expected_region_chain: &'static [&'static str],
+ }
+
+ // TODO: Consider loading these from a JSON file
+ const TEST_CASES: &[TestCase] = &[
+ TestCase {
+ input: "en-u-hc-h12-sd-usca",
+ requires_data: false,
+ extension_key: None,
+ fallback_supplement: None,
+ expected_language_chain: &["en-u-sd-usca", "en"],
+ expected_region_chain: &["en-u-sd-usca", "en", "und-u-sd-usca"],
+ },
+ TestCase {
+ input: "en-US-u-hc-h12-sd-usca",
+ requires_data: false,
+ extension_key: None,
+ fallback_supplement: None,
+ expected_language_chain: &["en-US-u-sd-usca", "en-US", "en"],
+ expected_region_chain: &["en-US-u-sd-usca", "en-US", "und-US-u-sd-usca", "und-US"],
+ },
+ TestCase {
+ input: "en-US-fonipa-u-hc-h12-sd-usca",
+ requires_data: false,
+ extension_key: Some(key!("hc")),
+ fallback_supplement: None,
+ expected_language_chain: &[
+ "en-US-fonipa-u-hc-h12-sd-usca",
+ "en-US-fonipa-u-sd-usca",
+ "en-US-fonipa",
+ "en-US",
+ "en",
+ ],
+ expected_region_chain: &[
+ "en-US-fonipa-u-hc-h12-sd-usca",
+ "en-US-fonipa-u-sd-usca",
+ "en-US-fonipa",
+ "en-US",
+ "und-US-fonipa-u-hc-h12-sd-usca",
+ "und-US-fonipa-u-sd-usca",
+ "und-US-fonipa",
+ "und-US",
+ ],
+ },
+ TestCase {
+ input: "en-u-hc-h12-sd-usca",
+ requires_data: true,
+ extension_key: None,
+ fallback_supplement: None,
+ expected_language_chain: &["en-u-sd-usca", "en"],
+ expected_region_chain: &["en-US-u-sd-usca", "en-US", "und-US-u-sd-usca", "und-US"],
+ },
+ TestCase {
+ input: "en-Latn-u-sd-usca",
+ requires_data: true,
+ extension_key: None,
+ fallback_supplement: None,
+ expected_language_chain: &["en-u-sd-usca", "en"],
+ expected_region_chain: &["en-US-u-sd-usca", "en-US", "und-US-u-sd-usca", "und-US"],
+ },
+ TestCase {
+ input: "en-Latn-US-u-sd-usca",
+ requires_data: true,
+ extension_key: None,
+ fallback_supplement: None,
+ expected_language_chain: &["en-US-u-sd-usca", "en-US", "en"],
+ expected_region_chain: &["en-US-u-sd-usca", "en-US", "und-US-u-sd-usca", "und-US"],
+ },
+ TestCase {
+ // NOTE: -u-rg is not yet supported; when it is, this test should be updated
+ input: "en-u-rg-gbxxxx",
+ requires_data: false,
+ extension_key: None,
+ fallback_supplement: None,
+ expected_language_chain: &["en"],
+ expected_region_chain: &["en"],
+ },
+ TestCase {
+ input: "sr-ME",
+ requires_data: true,
+ extension_key: None,
+ fallback_supplement: None,
+ expected_language_chain: &["sr-ME", "sr-Latn-ME", "sr-Latn"],
+ expected_region_chain: &["sr-ME", "und-ME"],
+ },
+ TestCase {
+ input: "sr-ME-fonipa",
+ requires_data: true,
+ extension_key: None,
+ fallback_supplement: None,
+ expected_language_chain: &[
+ "sr-ME-fonipa",
+ "sr-ME",
+ "sr-Latn-ME-fonipa",
+ "sr-Latn-ME",
+ "sr-Latn",
+ ],
+ expected_region_chain: &["sr-ME-fonipa", "sr-ME", "und-ME-fonipa", "und-ME"],
+ },
+ TestCase {
+ input: "de-Latn-LI",
+ requires_data: true,
+ extension_key: None,
+ fallback_supplement: None,
+ expected_language_chain: &["de-LI", "de"],
+ expected_region_chain: &["de-LI", "und-LI"],
+ },
+ TestCase {
+ input: "ca-ES-valencia",
+ requires_data: true,
+ extension_key: None,
+ fallback_supplement: None,
+ expected_language_chain: &["ca-ES-valencia", "ca-ES", "ca"],
+ expected_region_chain: &["ca-ES-valencia", "ca-ES", "und-ES-valencia", "und-ES"],
+ },
+ TestCase {
+ input: "es-AR",
+ requires_data: true,
+ extension_key: None,
+ fallback_supplement: None,
+ expected_language_chain: &["es-AR", "es-419", "es"],
+ expected_region_chain: &["es-AR", "und-AR"],
+ },
+ TestCase {
+ input: "hi-IN",
+ requires_data: true,
+ extension_key: None,
+ fallback_supplement: None,
+ expected_language_chain: &["hi-IN", "hi"],
+ expected_region_chain: &["hi-IN", "und-IN"],
+ },
+ TestCase {
+ input: "hi-Latn-IN",
+ requires_data: true,
+ extension_key: None,
+ fallback_supplement: None,
+ expected_language_chain: &["hi-Latn-IN", "hi-Latn", "en-IN", "en-001", "en"],
+ expected_region_chain: &["hi-Latn-IN", "und-IN"],
+ },
+ TestCase {
+ input: "yue-HK",
+ requires_data: true,
+ extension_key: None,
+ fallback_supplement: None,
+ expected_language_chain: &["yue-HK", "yue"],
+ expected_region_chain: &["yue-HK", "und-HK"],
+ },
+ TestCase {
+ input: "yue-HK",
+ requires_data: true,
+ extension_key: None,
+ fallback_supplement: Some(FallbackSupplement::Collation),
+ // TODO(#1964): add "zh" as a target.
+ expected_language_chain: &["yue-HK", "yue", "zh-Hant"],
+ expected_region_chain: &["yue-HK", "und-HK"],
+ },
+ ];
+
+ #[test]
+ #[cfg(feature = "serde")]
+ fn test_fallback() {
+ let fallbacker_no_data = LocaleFallbacker::new_without_data();
+ let fallbacker_with_data =
+ LocaleFallbacker::try_new_with_buffer_provider(&icu_testdata::buffer()).unwrap();
+ for cas in TEST_CASES {
+ for (priority, expected_chain) in [
+ (FallbackPriority::Language, cas.expected_language_chain),
+ (FallbackPriority::Region, cas.expected_region_chain),
+ ] {
+ let config = LocaleFallbackConfig {
+ priority,
+ extension_key: cas.extension_key,
+ fallback_supplement: cas.fallback_supplement,
+ };
+ let key_fallbacker = if cas.requires_data {
+ fallbacker_with_data.for_config(config)
+ } else {
+ fallbacker_no_data.for_config(config)
+ };
+ let locale = DataLocale::from(Locale::from_str(cas.input).unwrap());
+ let mut it = key_fallbacker.fallback_for(locale);
+ for expected in expected_chain {
+ assert_eq!(
+ expected,
+ &it.get().to_string(),
+ "{:?} ({:?})",
+ cas.input,
+ priority
+ );
+ it.step();
+ }
+ assert_eq!(
+ "und",
+ it.get().to_string(),
+ "{:?} ({:?})",
+ cas.input,
+ priority
+ );
+ }
+ }
+ }
+}
diff --git a/vendor/icu_provider_adapters/src/fallback/mod.rs b/vendor/icu_provider_adapters/src/fallback/mod.rs
new file mode 100644
index 000000000..6ec636a09
--- /dev/null
+++ b/vendor/icu_provider_adapters/src/fallback/mod.rs
@@ -0,0 +1,390 @@
+// 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 ).
+
+//! Tools for locale fallback, enabling arbitrary input locales to be mapped into the nearest
+//! locale with data.
+//!
+//! The algorithm implemented in this module is called [Flexible Vertical Fallback](
+//! https://docs.google.com/document/d/1Mp7EUyl-sFh_HZYgyeVwj88vJGpCBIWxzlCwGgLCDwM/edit).
+//! Watch [#2243](https://github.com/unicode-org/icu4x/issues/2243) to track improvements to
+//! this algorithm and steps to enshrine the algorithm in CLDR.
+//!
+//! # Examples
+//!
+//! Run the locale fallback algorithm:
+//!
+//! ```
+//! use icu_provider_adapters::fallback::LocaleFallbacker;
+//! use icu_provider::prelude::*;
+//!
+//! // Set up a LocaleFallbacker with data.
+//! let fallbacker = LocaleFallbacker::try_new_unstable(&icu_testdata::unstable()).expect("data");
+//!
+//! // Create a LocaleFallbackerWithConfig with a configuration for a specific key.
+//! // By default, uses language priority with no additional extension keywords.
+//! let key_fallbacker = fallbacker.for_config(Default::default());
+//!
+//! // Set up the fallback iterator.
+//! let mut fallback_iterator = key_fallbacker.fallback_for(icu_locid::locale!("hi-Latn-IN").into());
+//!
+//! // Run the algorithm and check the results.
+//! assert_eq!(fallback_iterator.get().to_string(), "hi-Latn-IN");
+//! fallback_iterator.step();
+//! assert_eq!(fallback_iterator.get().to_string(), "hi-Latn");
+//! fallback_iterator.step();
+//! assert_eq!(fallback_iterator.get().to_string(), "en-IN");
+//! fallback_iterator.step();
+//! assert_eq!(fallback_iterator.get().to_string(), "en-001");
+//! fallback_iterator.step();
+//! assert_eq!(fallback_iterator.get().to_string(), "en");
+//! fallback_iterator.step();
+//! assert_eq!(fallback_iterator.get().to_string(), "und");
+//! ```
+
+use icu_locid::extensions::unicode::{Key, Value};
+use icu_locid::subtags::Variants;
+use icu_provider::prelude::*;
+use icu_provider::FallbackPriority;
+use icu_provider::FallbackSupplement;
+
+mod adapter;
+mod algorithms;
+pub mod provider;
+
+pub use adapter::LocaleFallbackProvider;
+
+use provider::*;
+
+/// Configuration settings for a particular fallback operation.
+#[derive(Debug, Clone, PartialEq, Eq, Default)]
+#[non_exhaustive]
+pub struct LocaleFallbackConfig {
+ /// Strategy for choosing which subtags to drop during locale fallback.
+ ///
+ /// # Examples
+ ///
+ /// Retain the language and script subtags until the final step:
+ ///
+ /// ```
+ /// use icu_provider::prelude::*;
+ /// use icu_provider::FallbackPriority;
+ /// use icu_provider_adapters::fallback::LocaleFallbackConfig;
+ /// use icu_provider_adapters::fallback::LocaleFallbacker;
+ ///
+ /// // Set up the fallback iterator.
+ /// let fallbacker =
+ /// LocaleFallbacker::try_new_unstable(&icu_testdata::unstable())
+ /// .expect("data");
+ /// let mut config = LocaleFallbackConfig::default();
+ /// config.priority = FallbackPriority::Language;
+ /// let key_fallbacker = fallbacker.for_config(config);
+ /// let mut fallback_iterator = key_fallbacker
+ /// .fallback_for(icu_locid::locale!("ca-ES-valencia").into());
+ ///
+ /// // Run the algorithm and check the results.
+ /// assert_eq!(fallback_iterator.get().to_string(), "ca-ES-valencia");
+ /// fallback_iterator.step();
+ /// assert_eq!(fallback_iterator.get().to_string(), "ca-ES");
+ /// fallback_iterator.step();
+ /// assert_eq!(fallback_iterator.get().to_string(), "ca");
+ /// fallback_iterator.step();
+ /// assert_eq!(fallback_iterator.get().to_string(), "und");
+ /// ```
+ ///
+ /// Retain the region subtag until the final step:
+ ///
+ /// ```
+ /// use icu_provider::prelude::*;
+ /// use icu_provider::FallbackPriority;
+ /// use icu_provider_adapters::fallback::LocaleFallbackConfig;
+ /// use icu_provider_adapters::fallback::LocaleFallbacker;
+ ///
+ /// // Set up the fallback iterator.
+ /// let fallbacker =
+ /// LocaleFallbacker::try_new_unstable(&icu_testdata::unstable())
+ /// .expect("data");
+ /// let mut config = LocaleFallbackConfig::default();
+ /// config.priority = FallbackPriority::Region;
+ /// let key_fallbacker = fallbacker.for_config(config);
+ /// let mut fallback_iterator = key_fallbacker
+ /// .fallback_for(icu_locid::locale!("ca-ES-valencia").into());
+ ///
+ /// // Run the algorithm and check the results.
+ /// assert_eq!(fallback_iterator.get().to_string(), "ca-ES-valencia");
+ /// fallback_iterator.step();
+ /// assert_eq!(fallback_iterator.get().to_string(), "ca-ES");
+ /// fallback_iterator.step();
+ /// assert_eq!(fallback_iterator.get().to_string(), "und-ES-valencia");
+ /// fallback_iterator.step();
+ /// assert_eq!(fallback_iterator.get().to_string(), "und-ES");
+ /// fallback_iterator.step();
+ /// assert_eq!(fallback_iterator.get().to_string(), "und");
+ /// ```
+ pub priority: FallbackPriority,
+ /// An extension keyword to retain during locale fallback.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use icu_provider::prelude::*;
+ /// use icu_provider_adapters::fallback::LocaleFallbackConfig;
+ /// use icu_provider_adapters::fallback::LocaleFallbacker;
+ ///
+ /// // Set up the fallback iterator.
+ /// let fallbacker =
+ /// LocaleFallbacker::try_new_unstable(&icu_testdata::unstable())
+ /// .expect("data");
+ /// let mut config = LocaleFallbackConfig::default();
+ /// config.extension_key = Some(icu_locid::extensions_unicode_key!("nu"));
+ /// let key_fallbacker = fallbacker.for_config(config);
+ /// let mut fallback_iterator = key_fallbacker
+ /// .fallback_for(icu_locid::locale!("ar-EG-u-nu-latn").into());
+ ///
+ /// // Run the algorithm and check the results.
+ /// assert_eq!(fallback_iterator.get().to_string(), "ar-EG-u-nu-latn");
+ /// fallback_iterator.step();
+ /// assert_eq!(fallback_iterator.get().to_string(), "ar-EG");
+ /// fallback_iterator.step();
+ /// assert_eq!(fallback_iterator.get().to_string(), "ar");
+ /// fallback_iterator.step();
+ /// assert_eq!(fallback_iterator.get().to_string(), "und");
+ /// ```
+ pub extension_key: Option<Key>,
+ /// Fallback supplement data key to customize fallback rules.
+ ///
+ /// For example, most data keys for collation add additional parent locales, such as
+ /// "yue" to "zh-Hant", and data used for the `"-u-co"` extension keyword fallback.
+ ///
+ /// Currently the only supported fallback supplement is `FallbackSupplement::Collation`, but more may be
+ /// added in the future.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use icu_provider::prelude::*;
+ /// use icu_provider::FallbackPriority;
+ /// use icu_provider::FallbackSupplement;
+ /// use icu_provider_adapters::fallback::LocaleFallbackConfig;
+ /// use icu_provider_adapters::fallback::LocaleFallbacker;
+ /// use tinystr::tinystr;
+ ///
+ /// // Set up the fallback iterator.
+ /// let fallbacker =
+ /// LocaleFallbacker::try_new_unstable(&icu_testdata::unstable())
+ /// .expect("data");
+ /// let mut config = LocaleFallbackConfig::default();
+ /// config.priority = FallbackPriority::Collation;
+ /// config.fallback_supplement = Some(FallbackSupplement::Collation);
+ /// let key_fallbacker = fallbacker.for_config(config);
+ /// let mut fallback_iterator =
+ /// key_fallbacker.fallback_for(icu_locid::locale!("yue-HK").into());
+ ///
+ /// // Run the algorithm and check the results.
+ /// // TODO(#1964): add "zh" as a target.
+ /// assert_eq!(fallback_iterator.get().to_string(), "yue-HK");
+ /// fallback_iterator.step();
+ /// assert_eq!(fallback_iterator.get().to_string(), "yue");
+ /// fallback_iterator.step();
+ /// assert_eq!(fallback_iterator.get().to_string(), "zh-Hant");
+ /// fallback_iterator.step();
+ /// assert_eq!(fallback_iterator.get().to_string(), "und");
+ /// ```
+ pub fallback_supplement: Option<FallbackSupplement>,
+}
+
+/// Entry type for locale fallbacking.
+///
+/// See the module-level documentation for an example.
+#[derive(Debug, Clone, PartialEq)]
+pub struct LocaleFallbacker {
+ likely_subtags: DataPayload<LocaleFallbackLikelySubtagsV1Marker>,
+ parents: DataPayload<LocaleFallbackParentsV1Marker>,
+ collation_supplement: Option<DataPayload<CollationFallbackSupplementV1Marker>>,
+}
+
+/// Intermediate type for spawning locale fallback iterators based on a specific configuration.
+///
+/// See the module-level documentation for an example.
+#[derive(Debug, Clone, PartialEq)]
+pub struct LocaleFallbackerWithConfig<'a> {
+ likely_subtags: &'a LocaleFallbackLikelySubtagsV1<'a>,
+ parents: &'a LocaleFallbackParentsV1<'a>,
+ supplement: Option<&'a LocaleFallbackSupplementV1<'a>>,
+ config: LocaleFallbackConfig,
+}
+
+/// Inner iteration type. Does not own the item under fallback.
+struct LocaleFallbackIteratorInner<'a, 'b> {
+ likely_subtags: &'a LocaleFallbackLikelySubtagsV1<'a>,
+ parents: &'a LocaleFallbackParentsV1<'a>,
+ supplement: Option<&'a LocaleFallbackSupplementV1<'a>>,
+ config: &'b LocaleFallbackConfig,
+ backup_extension: Option<Value>,
+ backup_subdivision: Option<Value>,
+ backup_variants: Option<Variants>,
+}
+
+/// Iteration type for locale fallback operations.
+///
+/// Because the `Iterator` trait does not allow items to borrow from the iterator, this class does
+/// not implement that trait. Instead, use `.step()` and `.get()`.
+pub struct LocaleFallbackIterator<'a, 'b> {
+ current: DataLocale,
+ inner: LocaleFallbackIteratorInner<'a, 'b>,
+}
+
+impl LocaleFallbacker {
+ /// Creates a [`LocaleFallbacker`] with fallback data (likely subtags and parent locales).
+ ///
+ /// [📚 Help choosing a constructor](icu_provider::constructors)
+ /// <div class="stab unstable">
+ /// ⚠️ The bounds on this function may change over time, including in SemVer minor releases.
+ /// </div>
+ pub fn try_new_unstable<P>(provider: &P) -> Result<Self, DataError>
+ where
+ P: DataProvider<LocaleFallbackLikelySubtagsV1Marker>
+ + DataProvider<LocaleFallbackParentsV1Marker>
+ + DataProvider<CollationFallbackSupplementV1Marker>
+ + ?Sized,
+ {
+ let likely_subtags = provider.load(Default::default())?.take_payload()?;
+ let parents = provider.load(Default::default())?.take_payload()?;
+ let collation_supplement = match DataProvider::<CollationFallbackSupplementV1Marker>::load(
+ provider,
+ Default::default(),
+ ) {
+ Ok(response) => Some(response.take_payload()?),
+ // It is expected that not all keys are present
+ Err(DataError {
+ kind: DataErrorKind::MissingDataKey,
+ ..
+ }) => None,
+ Err(e) => return Err(e),
+ };
+ Ok(LocaleFallbacker {
+ likely_subtags,
+ parents,
+ collation_supplement,
+ })
+ }
+
+ icu_provider::gen_any_buffer_constructors!(locale: skip, options: skip, error: DataError);
+
+ /// Creates a [`LocaleFallbacker`] without fallback data. Using this constructor may result in
+ /// surprising behavior, especially in multi-script languages.
+ pub fn new_without_data() -> Self {
+ LocaleFallbacker {
+ likely_subtags: DataPayload::from_owned(Default::default()),
+ parents: DataPayload::from_owned(Default::default()),
+ collation_supplement: None,
+ }
+ }
+
+ /// Creates the intermediate [`LocaleFallbackerWithConfig`] with configuration options.
+ pub fn for_config(&self, config: LocaleFallbackConfig) -> LocaleFallbackerWithConfig {
+ let supplement = match config.fallback_supplement {
+ Some(FallbackSupplement::Collation) => {
+ self.collation_supplement.as_ref().map(|p| p.get())
+ }
+ _ => None,
+ };
+ LocaleFallbackerWithConfig {
+ likely_subtags: self.likely_subtags.get(),
+ parents: self.parents.get(),
+ supplement,
+ config,
+ }
+ }
+
+ /// Creates the intermediate [`LocaleFallbackerWithConfig`] based on a
+ /// [`DataKey`] and a [`DataRequestMetadata`].
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use icu_provider::prelude::*;
+ /// use icu_provider_adapters::fallback::LocaleFallbacker;
+ /// use std::borrow::Cow;
+ ///
+ /// // Define the data struct with key.
+ /// #[icu_provider::data_struct(marker(
+ /// FooV1Marker,
+ /// "demo/foo@1",
+ /// fallback_by = "region"
+ /// ))]
+ /// pub struct FooV1<'data> {
+ /// message: Cow<'data, str>,
+ /// };
+ ///
+ /// // Set up the fallback iterator.
+ /// let fallbacker =
+ /// LocaleFallbacker::try_new_unstable(&icu_testdata::unstable())
+ /// .expect("data");
+ /// let key_fallbacker = fallbacker.for_key(FooV1Marker::KEY);
+ /// let mut fallback_iterator =
+ /// key_fallbacker.fallback_for(icu_locid::locale!("en-GB").into());
+ ///
+ /// // Run the algorithm and check the results.
+ /// assert_eq!(fallback_iterator.get().to_string(), "en-GB");
+ /// fallback_iterator.step();
+ /// assert_eq!(fallback_iterator.get().to_string(), "und-GB");
+ /// fallback_iterator.step();
+ /// assert_eq!(fallback_iterator.get().to_string(), "und");
+ /// ```
+ ///
+ /// [`DataRequestMetadata`]: icu_provider::DataRequestMetadata
+ pub fn for_key(&self, data_key: DataKey) -> LocaleFallbackerWithConfig {
+ let priority = data_key.metadata().fallback_priority;
+ let extension_key = data_key.metadata().extension_key;
+ let fallback_supplement = data_key.metadata().fallback_supplement;
+ self.for_config(LocaleFallbackConfig {
+ priority,
+ extension_key,
+ fallback_supplement,
+ })
+ }
+}
+
+impl<'a> LocaleFallbackerWithConfig<'a> {
+ /// Creates an iterator based on a [`DataLocale`] (which can be created from [`Locale`]).
+ ///
+ /// When first initialized, the locale is normalized according to the fallback algorithm.
+ ///
+ /// [`Locale`]: icu_locid::Locale
+ pub fn fallback_for<'b>(&'b self, mut locale: DataLocale) -> LocaleFallbackIterator<'a, 'b> {
+ self.normalize(&mut locale);
+ LocaleFallbackIterator {
+ current: locale,
+ inner: LocaleFallbackIteratorInner {
+ likely_subtags: self.likely_subtags,
+ parents: self.parents,
+ supplement: self.supplement,
+ config: &self.config,
+ backup_extension: None,
+ backup_subdivision: None,
+ backup_variants: None,
+ },
+ }
+ }
+}
+
+impl LocaleFallbackIterator<'_, '_> {
+ /// Borrows the current [`DataLocale`] under fallback.
+ pub fn get(&self) -> &DataLocale {
+ &self.current
+ }
+
+ /// Takes the current [`DataLocale`] under fallback.
+ pub fn take(self) -> DataLocale {
+ self.current
+ }
+
+ /// Performs one step of the locale fallback algorithm.
+ ///
+ /// The fallback is completed once the inner [`DataLocale`] becomes `und`.
+ pub fn step(&mut self) -> &mut Self {
+ self.inner.step(&mut self.current);
+ self
+ }
+}
diff --git a/vendor/icu_provider_adapters/src/fallback/provider.rs b/vendor/icu_provider_adapters/src/fallback/provider.rs
new file mode 100644
index 000000000..edfa01f58
--- /dev/null
+++ b/vendor/icu_provider_adapters/src/fallback/provider.rs
@@ -0,0 +1,111 @@
+// 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 ).
+
+//! Data provider struct definitions for vertical fallback.
+//!
+//! Read more about data providers: [`icu_provider`]
+
+// Provider structs must be stable
+#![allow(clippy::exhaustive_structs)]
+
+use icu_locid::extensions::unicode::Key;
+use icu_locid::subtags::{Language, Region, Script};
+use icu_locid::{subtags_region as region, subtags_script as script};
+use tinystr::TinyAsciiStr;
+
+use icu_provider::prelude::*;
+
+use zerovec::ule::UnvalidatedStr;
+use zerovec::ZeroMap;
+use zerovec::ZeroMap2d;
+
+// We use raw TinyAsciiStrs for map keys, as we then don't have to
+// validate them as subtags on deserialization. Map lookup can be
+// done even if they are not valid tags (an invalid key will just
+// become inaccessible).
+type UnvalidatedLanguage = TinyAsciiStr<3>;
+type UnvalidatedScript = TinyAsciiStr<4>;
+type UnvalidatedRegion = TinyAsciiStr<3>;
+
+/// Locale fallback rules derived from likely subtags data.
+#[icu_provider::data_struct(LocaleFallbackLikelySubtagsV1Marker = "fallback/likelysubtags@1")]
+#[derive(Default, Clone, PartialEq, Debug)]
+#[cfg_attr(
+ feature = "datagen",
+ derive(serde::Serialize, databake::Bake),
+ databake(path = icu_provider_adapters::fallback::provider),
+)]
+#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
+#[yoke(prove_covariance_manually)]
+pub struct LocaleFallbackLikelySubtagsV1<'data> {
+ /// Map from language to the default script in that language. Languages whose default script
+ /// is `Latn` are not included in the map for data size savings.
+ ///
+ /// Example: "zh" defaults to "Hans", which is in this map.
+ #[cfg_attr(feature = "serde", serde(borrow))]
+ pub l2s: ZeroMap<'data, UnvalidatedLanguage, Script>,
+ /// Map from language-region pairs to a script. Only populated if the script is different
+ /// from the one in `l2s` for that language.
+ ///
+ /// Example: "zh-TW" defaults to "Hant", which is in this map.
+ #[cfg_attr(feature = "serde", serde(borrow))]
+ pub lr2s: ZeroMap2d<'data, UnvalidatedLanguage, UnvalidatedRegion, Script>,
+ /// Map from language to the default region in that language. Languages whose default region
+ /// is `ZZ` are not included in the map for data size savings.
+ ///
+ /// Example: "zh" defaults to "CN".
+ #[cfg_attr(feature = "serde", serde(borrow))]
+ pub l2r: ZeroMap<'data, UnvalidatedLanguage, Region>,
+ /// Map from language-script pairs to a region. Only populated if the region is different
+ /// from the one in `l2r` for that language.
+ ///
+ /// Example: "zh-Hant" defaults to "TW".
+ #[cfg_attr(feature = "serde", serde(borrow))]
+ pub ls2r: ZeroMap2d<'data, UnvalidatedLanguage, UnvalidatedScript, Region>,
+}
+
+/// `Latn` is the most common script, so it is defaulted for data size savings.
+pub const DEFAULT_SCRIPT: Script = script!("Latn");
+
+/// `ZZ` is the most common region, so it is defaulted for data size savings.
+pub const DEFAULT_REGION: Region = region!("ZZ");
+
+/// Locale fallback rules derived from CLDR parent locales data.
+#[icu_provider::data_struct(LocaleFallbackParentsV1Marker = "fallback/parents@1")]
+#[derive(Default, Clone, PartialEq, Debug)]
+#[cfg_attr(
+ feature = "datagen",
+ derive(serde::Serialize, databake::Bake),
+ databake(path = icu_provider_adapters::fallback::provider),
+)]
+#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
+#[yoke(prove_covariance_manually)]
+pub struct LocaleFallbackParentsV1<'data> {
+ /// Map from language identifier to language identifier, indicating that the language on the
+ /// left should inherit from the language on the right.
+ #[cfg_attr(feature = "serde", serde(borrow))]
+ pub parents: ZeroMap<'data, UnvalidatedStr, (Language, Option<Script>, Option<Region>)>,
+}
+
+/// Key-specific supplemental fallback data.
+#[icu_provider::data_struct(marker(
+ CollationFallbackSupplementV1Marker,
+ "fallback/supplement/co@1"
+))]
+#[derive(Default, Clone, PartialEq, Debug)]
+#[cfg_attr(
+ feature = "datagen",
+ derive(serde::Serialize, databake::Bake),
+ databake(path = icu_provider_adapters::fallback::provider),
+)]
+#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
+#[yoke(prove_covariance_manually)]
+pub struct LocaleFallbackSupplementV1<'data> {
+ /// Additional parent locales to supplement the common ones.
+ #[cfg_attr(feature = "serde", serde(borrow))]
+ pub parents: ZeroMap<'data, UnvalidatedStr, (Language, Option<Script>, Option<Region>)>,
+ /// Default values for Unicode extension keywords.
+ #[cfg_attr(feature = "serde", serde(borrow))]
+ pub unicode_extension_defaults: ZeroMap2d<'data, Key, UnvalidatedStr, UnvalidatedStr>,
+}
diff --git a/vendor/icu_provider_adapters/src/filter/impls.rs b/vendor/icu_provider_adapters/src/filter/impls.rs
new file mode 100644
index 000000000..af8ea7adf
--- /dev/null
+++ b/vendor/icu_provider_adapters/src/filter/impls.rs
@@ -0,0 +1,215 @@
+// 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 super::*;
+use alloc::boxed::Box;
+use icu_provider::prelude::*;
+
+use icu_locid::LanguageIdentifier;
+
+impl<D, F> RequestFilterDataProvider<D, F>
+where
+ F: Fn(DataRequest) -> bool + Sync,
+{
+ /// Filter out data requests with certain langids according to the predicate function. The
+ /// predicate should return `true` to allow a langid and `false` to reject a langid.
+ ///
+ /// Data requests with no langid will be allowed. To reject data requests without a langid,
+ /// chain this with [`Self::require_langid`].
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use icu_locid::LanguageIdentifier;
+ /// use icu_locid::{langid, locale, subtags_language as language};
+ /// use icu_provider::datagen::*;
+ /// use icu_provider::hello_world::*;
+ /// use icu_provider::prelude::*;
+ /// use icu_provider_adapters::filter::Filterable;
+ ///
+ /// let provider = HelloWorldProvider
+ /// .filterable("Demo no-English filter")
+ /// .filter_by_langid(|langid| langid.language != language!("en"));
+ ///
+ /// // German requests should succeed:
+ /// let req_de = DataRequest {
+ /// locale: &locale!("de").into(),
+ /// metadata: Default::default(),
+ /// };
+ /// let response: Result<DataResponse<HelloWorldV1Marker>, _> =
+ /// provider.load(req_de);
+ /// assert!(matches!(response, Ok(_)));
+ ///
+ /// // English requests should fail:
+ /// let req_en = DataRequest {
+ /// locale: &locale!("en-US").into(),
+ /// metadata: Default::default(),
+ /// };
+ /// let response: Result<DataResponse<HelloWorldV1Marker>, _> =
+ /// provider.load(req_en);
+ /// assert!(matches!(
+ /// response,
+ /// Err(DataError {
+ /// kind: DataErrorKind::FilteredResource,
+ /// ..
+ /// })
+ /// ));
+ ///
+ /// // English should not appear in the iterator result:
+ /// let supported_langids = provider
+ /// .supported_locales()
+ /// .expect("Should successfully make an iterator of supported locales")
+ /// .into_iter()
+ /// .map(|options| options.get_langid())
+ /// .collect::<Vec<LanguageIdentifier>>();
+ /// assert!(supported_langids.contains(&langid!("de")));
+ /// assert!(!supported_langids.contains(&langid!("en")));
+ /// ```
+ pub fn filter_by_langid<'a>(
+ self,
+ predicate: impl Fn(&LanguageIdentifier) -> bool + Sync + 'a,
+ ) -> RequestFilterDataProvider<D, Box<dyn Fn(DataRequest) -> bool + Sync + 'a>>
+ where
+ F: 'a,
+ {
+ let old_predicate = self.predicate;
+ RequestFilterDataProvider {
+ inner: self.inner,
+ predicate: Box::new(move |request| -> bool {
+ if !(old_predicate)(request) {
+ return false;
+ }
+ predicate(&request.locale.get_langid())
+ }),
+ filter_name: self.filter_name,
+ }
+ }
+
+ /// Filter out data request except those having a language identifier that exactly matches
+ /// one in the allowlist.
+ ///
+ /// This will be replaced with a smarter algorithm for locale filtering; see
+ /// <https://github.com/unicode-org/icu4x/issues/834>
+ ///
+ /// Data requests with no langid will be allowed. To reject data requests without a langid,
+ /// chain this with [`Self::require_langid`].
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use icu_locid::{langid, locale};
+ /// use icu_provider::hello_world::*;
+ /// use icu_provider::prelude::*;
+ /// use icu_provider_adapters::filter::Filterable;
+ ///
+ /// let allowlist = vec![langid!("de"), langid!("zh")];
+ /// let provider = HelloWorldProvider
+ /// .filterable("Demo German+Chinese filter")
+ /// .filter_by_langid_allowlist_strict(&allowlist);
+ ///
+ /// // German requests should succeed:
+ /// let req_de = DataRequest {
+ /// locale: &locale!("de").into(),
+ /// metadata: Default::default(),
+ /// };
+ /// let response: Result<DataResponse<HelloWorldV1Marker>, _> =
+ /// provider.load(req_de);
+ /// assert!(matches!(response, Ok(_)));
+ ///
+ /// // English requests should fail:
+ /// let req_en = DataRequest {
+ /// locale: &locale!("en-US").into(),
+ /// metadata: Default::default(),
+ /// };
+ /// let response: Result<DataResponse<HelloWorldV1Marker>, _> =
+ /// provider.load(req_en);
+ /// assert!(matches!(
+ /// response,
+ /// Err(DataError {
+ /// kind: DataErrorKind::FilteredResource,
+ /// ..
+ /// })
+ /// ));
+ /// assert_eq!(
+ /// response.unwrap_err().str_context,
+ /// Some("Demo German+Chinese filter")
+ /// );
+ /// ```
+ pub fn filter_by_langid_allowlist_strict<'a>(
+ self,
+ allowlist: &'a [LanguageIdentifier],
+ ) -> RequestFilterDataProvider<D, Box<dyn Fn(DataRequest) -> bool + Sync + 'a>>
+ where
+ F: 'a,
+ {
+ let old_predicate = self.predicate;
+ RequestFilterDataProvider {
+ inner: self.inner,
+ predicate: Box::new(move |request| -> bool {
+ if !(old_predicate)(request) {
+ return false;
+ }
+ request.locale.is_langid_und() || allowlist.contains(&request.locale.get_langid())
+ }),
+ filter_name: self.filter_name,
+ }
+ }
+
+ /// Require that data requests contain a langid.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use icu_locid::locale;
+ /// use icu_provider::hello_world::*;
+ /// use icu_provider::prelude::*;
+ /// use icu_provider_adapters::filter::Filterable;
+ ///
+ /// let provider = HelloWorldProvider
+ /// .filterable("Demo require-langid filter")
+ /// .require_langid();
+ ///
+ /// // Requests with a langid should succeed:
+ /// let req_with_langid = DataRequest {
+ /// locale: &locale!("de").into(),
+ /// metadata: Default::default(),
+ /// };
+ /// let response: Result<DataResponse<HelloWorldV1Marker>, _> =
+ /// provider.load(req_with_langid);
+ /// assert!(matches!(response, Ok(_)));
+ ///
+ /// // Requests without a langid should fail:
+ /// let req_no_langid = DataRequest {
+ /// locale: Default::default(),
+ /// metadata: Default::default(),
+ /// };
+ /// let response: Result<DataResponse<HelloWorldV1Marker>, _> =
+ /// provider.load(req_no_langid);
+ /// assert!(matches!(
+ /// response,
+ /// Err(DataError {
+ /// kind: DataErrorKind::FilteredResource,
+ /// ..
+ /// })
+ /// ));
+ /// ```
+ pub fn require_langid<'a>(
+ self,
+ ) -> RequestFilterDataProvider<D, Box<dyn Fn(DataRequest) -> bool + Sync + 'a>>
+ where
+ F: 'a,
+ {
+ let old_predicate = self.predicate;
+ RequestFilterDataProvider {
+ inner: self.inner,
+ predicate: Box::new(move |request| -> bool {
+ if !(old_predicate)(request) {
+ return false;
+ }
+ !request.locale.is_langid_und()
+ }),
+ filter_name: self.filter_name,
+ }
+ }
+}
diff --git a/vendor/icu_provider_adapters/src/filter/mod.rs b/vendor/icu_provider_adapters/src/filter/mod.rs
new file mode 100644
index 000000000..42516b82e
--- /dev/null
+++ b/vendor/icu_provider_adapters/src/filter/mod.rs
@@ -0,0 +1,242 @@
+// 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 ).
+
+//! Providers that filter resource requests.
+//!
+//! Requests that fail a filter test will return [`DataError`] of kind [`FilteredResource`](
+//! DataErrorKind::FilteredResource) and will not appear in [`IterableDynamicDataProvider`] iterators.
+//!
+//! The main struct is [`RequestFilterDataProvider`]. Although that struct can be created
+//! directly, the traits in this module provide helper functions for common filtering patterns.
+//!
+//! To create a `RequestFilterDataProvider`, you can use the [`Filterable`] blanket function:
+//!
+//! ```
+//! use icu_provider_adapters::filter::Filterable;
+//!
+//! // now call .filterable() on any object to get a RequestFilterDataProvider
+//! ```
+//!
+//! # Examples
+//!
+//! ```
+//! use icu_locid::subtags_language as language;
+//! use icu_provider::hello_world::*;
+//! use icu_provider::prelude::*;
+//! use icu_provider_adapters::filter::Filterable;
+//!
+//! // Only return German data from a HelloWorldProvider:
+//! HelloWorldProvider
+//! .filterable("Demo German-only filter")
+//! .filter_by_langid(|langid| langid.language == language!("de"));
+//! ```
+//!
+//! [`IterableDynamicDataProvider`]: icu_provider::datagen::IterableDynamicDataProvider
+
+mod impls;
+
+pub use impls::*;
+
+#[cfg(feature = "datagen")]
+use icu_provider::datagen;
+use icu_provider::prelude::*;
+
+/// A data provider that selectively filters out data requests.
+///
+/// Data requests that are rejected by the filter will return a [`DataError`] with kind
+/// [`FilteredResource`](DataErrorKind::FilteredResource), and they will not be returned
+/// by [`datagen::IterableDynamicDataProvider::supported_locales_for_key`].
+///
+/// Although this struct can be created directly, the traits in this module provide helper
+/// functions for common filtering patterns.
+#[allow(clippy::exhaustive_structs)] // this type is stable
+pub struct RequestFilterDataProvider<D, F>
+where
+ F: Fn(DataRequest) -> bool,
+{
+ /// The data provider to which we delegate requests.
+ pub inner: D,
+
+ /// The predicate function. A return value of `true` indicates that the request should
+ /// proceed as normal; a return value of `false` will reject the request.
+ pub predicate: F,
+
+ /// A name for this filter, used in error messages.
+ pub filter_name: &'static str,
+}
+
+impl<D, F, M> DynamicDataProvider<M> for RequestFilterDataProvider<D, F>
+where
+ F: Fn(DataRequest) -> bool,
+ M: DataMarker,
+ D: DynamicDataProvider<M>,
+{
+ fn load_data(&self, key: DataKey, req: DataRequest) -> Result<DataResponse<M>, DataError> {
+ if (self.predicate)(req) {
+ self.inner.load_data(key, req)
+ } else {
+ Err(DataErrorKind::FilteredResource
+ .with_str_context(self.filter_name)
+ .with_req(key, req))
+ }
+ }
+}
+
+impl<D, F, M> DataProvider<M> for RequestFilterDataProvider<D, F>
+where
+ F: Fn(DataRequest) -> bool,
+ M: KeyedDataMarker,
+ D: DataProvider<M>,
+{
+ fn load(&self, req: DataRequest) -> Result<DataResponse<M>, DataError> {
+ if (self.predicate)(req) {
+ self.inner.load(req)
+ } else {
+ Err(DataErrorKind::FilteredResource
+ .with_str_context(self.filter_name)
+ .with_req(M::KEY, req))
+ }
+ }
+}
+
+impl<D, F> BufferProvider for RequestFilterDataProvider<D, F>
+where
+ F: Fn(DataRequest) -> bool,
+ D: BufferProvider,
+{
+ fn load_buffer(
+ &self,
+ key: DataKey,
+ req: DataRequest,
+ ) -> Result<DataResponse<BufferMarker>, DataError> {
+ if (self.predicate)(req) {
+ self.inner.load_buffer(key, req)
+ } else {
+ Err(DataErrorKind::FilteredResource
+ .with_str_context(self.filter_name)
+ .with_req(key, req))
+ }
+ }
+}
+
+impl<D, F> AnyProvider for RequestFilterDataProvider<D, F>
+where
+ F: Fn(DataRequest) -> bool,
+ D: AnyProvider,
+{
+ fn load_any(&self, key: DataKey, req: DataRequest) -> Result<AnyResponse, DataError> {
+ if (self.predicate)(req) {
+ self.inner.load_any(key, req)
+ } else {
+ Err(DataErrorKind::FilteredResource
+ .with_str_context(self.filter_name)
+ .with_req(key, req))
+ }
+ }
+}
+
+#[cfg(feature = "datagen")]
+impl<M, D, F> datagen::IterableDynamicDataProvider<M> for RequestFilterDataProvider<D, F>
+where
+ M: DataMarker,
+ F: Fn(DataRequest) -> bool,
+ D: datagen::IterableDynamicDataProvider<M>,
+{
+ fn supported_locales_for_key(
+ &self,
+ key: DataKey,
+ ) -> Result<alloc::vec::Vec<DataLocale>, DataError> {
+ self.inner.supported_locales_for_key(key).map(|vec| {
+ // Use filter_map instead of filter to avoid cloning the locale
+ vec.into_iter()
+ .filter_map(|locale| {
+ if (self.predicate)(DataRequest {
+ locale: &locale,
+ metadata: Default::default(),
+ }) {
+ Some(locale)
+ } else {
+ None
+ }
+ })
+ .collect()
+ })
+ }
+}
+
+#[cfg(feature = "datagen")]
+impl<M, D, F> datagen::IterableDataProvider<M> for RequestFilterDataProvider<D, F>
+where
+ M: KeyedDataMarker,
+ F: Fn(DataRequest) -> bool,
+ D: datagen::IterableDataProvider<M>,
+{
+ fn supported_locales(&self) -> Result<alloc::vec::Vec<DataLocale>, DataError> {
+ self.inner.supported_locales().map(|vec| {
+ // Use filter_map instead of filter to avoid cloning the locale
+ vec.into_iter()
+ .filter_map(|locale| {
+ if (self.predicate)(DataRequest {
+ locale: &locale,
+ metadata: Default::default(),
+ }) {
+ Some(locale)
+ } else {
+ None
+ }
+ })
+ .collect()
+ })
+ }
+}
+
+#[cfg(feature = "datagen")]
+impl<D, F, MFrom, MTo> datagen::DataConverter<MFrom, MTo> for RequestFilterDataProvider<D, F>
+where
+ D: datagen::DataConverter<MFrom, MTo>,
+ MFrom: DataMarker,
+ MTo: DataMarker,
+ F: Fn(DataRequest) -> bool,
+{
+ fn convert(
+ &self,
+ key: DataKey,
+ from: DataPayload<MFrom>,
+ ) -> Result<DataPayload<MTo>, (DataPayload<MFrom>, DataError)> {
+ // Conversions are type-agnostic
+ self.inner.convert(key, from)
+ }
+}
+
+/// A blanket-implemented trait exposing the [`Self::filterable()`] function.
+///
+/// For more details, see [`icu_provider_adapters::filter`](crate::filter).
+pub trait Filterable: Sized {
+ /// Creates a filterable data provider with the given name for debugging.
+ ///
+ /// For more details, see [`icu_provider_adapters::filter`](crate::filter).
+ fn filterable(
+ self,
+ filter_name: &'static str,
+ ) -> RequestFilterDataProvider<Self, fn(DataRequest) -> bool>;
+}
+
+impl<T> Filterable for T
+where
+ T: Sized,
+{
+ fn filterable(
+ self,
+ filter_name: &'static str,
+ ) -> RequestFilterDataProvider<Self, fn(DataRequest) -> bool> {
+ fn noop(_: DataRequest) -> bool {
+ true
+ }
+ RequestFilterDataProvider {
+ inner: self,
+ predicate: noop,
+ filter_name,
+ }
+ }
+}
diff --git a/vendor/icu_provider_adapters/src/fork/by_error.rs b/vendor/icu_provider_adapters/src/fork/by_error.rs
new file mode 100644
index 000000000..5069229fa
--- /dev/null
+++ b/vendor/icu_provider_adapters/src/fork/by_error.rs
@@ -0,0 +1,287 @@
+// 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 super::ForkByErrorPredicate;
+use alloc::vec::Vec;
+#[cfg(feature = "datagen")]
+use icu_provider::datagen;
+use icu_provider::prelude::*;
+
+/// A provider that returns data from one of two child providers based on a predicate function.
+///
+/// This is an abstract forking provider that must be provided with a type implementing the
+/// [`ForkByErrorPredicate`] trait.
+///
+/// [`ForkByErrorProvider`] does not support forking between [`DataProvider`]s. However, it
+/// supports forking between [`AnyProvider`], [`BufferProvider`], and [`DynamicDataProvider`].
+#[derive(Debug, PartialEq, Eq)]
+pub struct ForkByErrorProvider<P0, P1, F>(P0, P1, F);
+
+impl<P0, P1, F> ForkByErrorProvider<P0, P1, F> {
+ /// Create a new provider that forks between the two children.
+ ///
+ /// The `predicate` argument should be an instance of a struct implementing
+ /// [`ForkByErrorPredicate`].
+ pub fn new_with_predicate(p0: P0, p1: P1, predicate: F) -> Self {
+ Self(p0, p1, predicate)
+ }
+
+ /// Returns references to the inner providers.
+ pub fn inner(&self) -> (&P0, &P1) {
+ (&self.0, &self.1)
+ }
+
+ /// Returns ownership of the inner providers to the caller.
+ pub fn into_inner(self) -> (P0, P1) {
+ (self.0, self.1)
+ }
+}
+
+impl<P0, P1, F> BufferProvider for ForkByErrorProvider<P0, P1, F>
+where
+ P0: BufferProvider,
+ P1: BufferProvider,
+ F: ForkByErrorPredicate,
+{
+ fn load_buffer(
+ &self,
+ key: DataKey,
+ req: DataRequest,
+ ) -> Result<DataResponse<BufferMarker>, DataError> {
+ let result = self.0.load_buffer(key, req);
+ match result {
+ Ok(ok) => return Ok(ok),
+ Err(err) if !self.2.test(key, Some(req), err) => return Err(err),
+ _ => (),
+ };
+ self.1.load_buffer(key, req)
+ }
+}
+
+impl<P0, P1, F> AnyProvider for ForkByErrorProvider<P0, P1, F>
+where
+ P0: AnyProvider,
+ P1: AnyProvider,
+ F: ForkByErrorPredicate,
+{
+ fn load_any(&self, key: DataKey, req: DataRequest) -> Result<AnyResponse, DataError> {
+ let result = self.0.load_any(key, req);
+ match result {
+ Ok(ok) => return Ok(ok),
+ Err(err) if !self.2.test(key, Some(req), err) => return Err(err),
+ _ => (),
+ };
+ self.1.load_any(key, req)
+ }
+}
+
+impl<M, P0, P1, F> DynamicDataProvider<M> for ForkByErrorProvider<P0, P1, F>
+where
+ M: DataMarker,
+ P0: DynamicDataProvider<M>,
+ P1: DynamicDataProvider<M>,
+ F: ForkByErrorPredicate,
+{
+ fn load_data(&self, key: DataKey, req: DataRequest) -> Result<DataResponse<M>, DataError> {
+ let result = self.0.load_data(key, req);
+ match result {
+ Ok(ok) => return Ok(ok),
+ Err(err) if !self.2.test(key, Some(req), err) => return Err(err),
+ _ => (),
+ };
+ self.1.load_data(key, req)
+ }
+}
+
+#[cfg(feature = "datagen")]
+impl<M, P0, P1, F> datagen::IterableDynamicDataProvider<M> for ForkByErrorProvider<P0, P1, F>
+where
+ M: DataMarker,
+ P0: datagen::IterableDynamicDataProvider<M>,
+ P1: datagen::IterableDynamicDataProvider<M>,
+ F: ForkByErrorPredicate,
+{
+ fn supported_locales_for_key(&self, key: DataKey) -> Result<Vec<DataLocale>, DataError> {
+ let result = self.0.supported_locales_for_key(key);
+ match result {
+ Ok(ok) => return Ok(ok),
+ Err(err) if !self.2.test(key, None, err) => return Err(err),
+ _ => (),
+ };
+ self.1.supported_locales_for_key(key)
+ }
+}
+
+/// A provider that returns data from the first child provider passing a predicate function.
+///
+/// This is an abstract forking provider that must be provided with a type implementing the
+/// [`ForkByErrorPredicate`] trait.
+///
+/// [`MultiForkByErrorProvider`] does not support forking between [`DataProvider`]s. However, it
+/// supports forking between [`AnyProvider`], [`BufferProvider`], and [`DynamicDataProvider`].
+pub struct MultiForkByErrorProvider<P, F> {
+ providers: Vec<P>,
+ predicate: F,
+}
+
+impl<P, F> MultiForkByErrorProvider<P, F> {
+ /// Create a new provider that forks between the vector of children.
+ ///
+ /// The `predicate` argument should be an instance of a struct implementing
+ /// [`ForkByErrorPredicate`].
+ pub fn new_with_predicate(providers: Vec<P>, predicate: F) -> Self {
+ Self {
+ providers,
+ predicate,
+ }
+ }
+
+ /// Returns a slice of the inner providers.
+ pub fn inner(&self) -> &[P] {
+ &self.providers
+ }
+
+ /// Returns ownership of the inner providers to the caller.
+ pub fn into_inner(self) -> Vec<P> {
+ self.providers
+ }
+}
+
+impl<P, F> BufferProvider for MultiForkByErrorProvider<P, F>
+where
+ P: BufferProvider,
+ F: ForkByErrorPredicate,
+{
+ fn load_buffer(
+ &self,
+ key: DataKey,
+ req: DataRequest,
+ ) -> Result<DataResponse<BufferMarker>, DataError> {
+ for provider in self.providers.iter() {
+ let result = provider.load_buffer(key, req);
+ match result {
+ Ok(ok) => return Ok(ok),
+ Err(err) if !self.predicate.test(key, Some(req), err) => return Err(err),
+ _ => (),
+ };
+ }
+ Err(DataErrorKind::MissingDataKey.with_key(key))
+ }
+}
+
+impl<P, F> AnyProvider for MultiForkByErrorProvider<P, F>
+where
+ P: AnyProvider,
+ F: ForkByErrorPredicate,
+{
+ fn load_any(&self, key: DataKey, req: DataRequest) -> Result<AnyResponse, DataError> {
+ for provider in self.providers.iter() {
+ let result = provider.load_any(key, req);
+ match result {
+ Ok(ok) => return Ok(ok),
+ Err(err) if !self.predicate.test(key, Some(req), err) => return Err(err),
+ _ => (),
+ };
+ }
+ Err(DataErrorKind::MissingDataKey.with_key(key))
+ }
+}
+
+impl<M, P, F> DynamicDataProvider<M> for MultiForkByErrorProvider<P, F>
+where
+ M: DataMarker,
+ P: DynamicDataProvider<M>,
+ F: ForkByErrorPredicate,
+{
+ fn load_data(&self, key: DataKey, req: DataRequest) -> Result<DataResponse<M>, DataError> {
+ for provider in self.providers.iter() {
+ let result = provider.load_data(key, req);
+ match result {
+ Ok(ok) => return Ok(ok),
+ Err(err) if !self.predicate.test(key, Some(req), err) => return Err(err),
+ _ => (),
+ };
+ }
+ Err(DataErrorKind::MissingDataKey.with_key(key))
+ }
+}
+
+#[cfg(feature = "datagen")]
+impl<M, P, F> datagen::IterableDynamicDataProvider<M> for MultiForkByErrorProvider<P, F>
+where
+ M: DataMarker,
+ P: datagen::IterableDynamicDataProvider<M>,
+ F: ForkByErrorPredicate,
+{
+ fn supported_locales_for_key(&self, key: DataKey) -> Result<Vec<DataLocale>, DataError> {
+ for provider in self.providers.iter() {
+ let result = provider.supported_locales_for_key(key);
+ match result {
+ Ok(ok) => return Ok(ok),
+ Err(err) if !self.predicate.test(key, None, err) => return Err(err),
+ _ => (),
+ };
+ }
+ Err(DataErrorKind::MissingDataKey.with_key(key))
+ }
+}
+
+#[cfg(feature = "datagen")]
+impl<P, MFrom, MTo, F> datagen::DataConverter<MFrom, MTo> for MultiForkByErrorProvider<P, F>
+where
+ P: datagen::DataConverter<MFrom, MTo>,
+ F: ForkByErrorPredicate,
+ MFrom: DataMarker,
+ MTo: DataMarker,
+{
+ fn convert(
+ &self,
+ key: DataKey,
+ mut from: DataPayload<MFrom>,
+ ) -> Result<DataPayload<MTo>, (DataPayload<MFrom>, DataError)> {
+ for provider in self.providers.iter() {
+ let result = provider.convert(key, from);
+ match result {
+ Ok(ok) => return Ok(ok),
+ Err(e) => {
+ let (returned, err) = e;
+ if !self.predicate.test(key, None, err) {
+ return Err((returned, err));
+ }
+ from = returned;
+ }
+ };
+ }
+ Err((from, DataErrorKind::MissingDataKey.with_key(key)))
+ }
+}
+
+#[cfg(feature = "datagen")]
+impl<P0, P1, F, MFrom, MTo> datagen::DataConverter<MFrom, MTo> for ForkByErrorProvider<P0, P1, F>
+where
+ P0: datagen::DataConverter<MFrom, MTo>,
+ P1: datagen::DataConverter<MFrom, MTo>,
+ F: ForkByErrorPredicate,
+ MFrom: DataMarker,
+ MTo: DataMarker,
+{
+ fn convert(
+ &self,
+ key: DataKey,
+ mut from: DataPayload<MFrom>,
+ ) -> Result<DataPayload<MTo>, (DataPayload<MFrom>, DataError)> {
+ let result = self.0.convert(key, from);
+ match result {
+ Ok(ok) => return Ok(ok),
+ Err(e) => {
+ let (returned, err) = e;
+ if !self.2.test(key, None, err) {
+ return Err((returned, err));
+ }
+ from = returned;
+ }
+ };
+ self.1.convert(key, from)
+ }
+}
diff --git a/vendor/icu_provider_adapters/src/fork/macros.rs b/vendor/icu_provider_adapters/src/fork/macros.rs
new file mode 100644
index 000000000..7c18a9ba3
--- /dev/null
+++ b/vendor/icu_provider_adapters/src/fork/macros.rs
@@ -0,0 +1,71 @@
+// 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 ).
+
+/// Make a forking data provider with an arbitrary number of inner providers
+/// that are known at build time.
+///
+/// # Examples
+///
+/// ```
+/// use icu_provider_adapters::fork::ForkByKeyProvider;
+///
+/// // Some empty example providers:
+/// #[derive(Default, PartialEq, Debug)]
+/// struct Provider1;
+/// #[derive(Default, PartialEq, Debug)]
+/// struct Provider2;
+/// #[derive(Default, PartialEq, Debug)]
+/// struct Provider3;
+///
+/// // Combine them into one:
+/// let forking1 = icu_provider_adapters::make_forking_provider!(
+/// ForkByKeyProvider::new,
+/// [
+/// Provider1::default(),
+/// Provider2::default(),
+/// Provider3::default(),
+/// ]
+/// );
+///
+/// // This is equivalent to:
+/// let forking2 = ForkByKeyProvider::new(
+/// Provider1::default(),
+/// ForkByKeyProvider::new(Provider2::default(), Provider3::default()),
+/// );
+///
+/// assert_eq!(forking1, forking2);
+/// ```
+#[macro_export]
+macro_rules! make_forking_provider {
+ // Base case:
+ ($combo_p:path, [ $a:expr, $b:expr, ]) => {
+ $combo_p($a, $b)
+ };
+ // General list:
+ ($combo_p:path, [ $a:expr, $b:expr, $($c:expr),+, ]) => {
+ $combo_p($a, $crate::make_forking_provider!($combo_p, [ $b, $($c),+, ]))
+ };
+}
+
+#[cfg(test)]
+mod test {
+ #[derive(Default)]
+ struct Provider1;
+ #[derive(Default)]
+ struct Provider2;
+ #[derive(Default)]
+ struct Provider3;
+
+ #[test]
+ fn test_make_forking_provider() {
+ make_forking_provider!(
+ crate::fork::ForkByKeyProvider::new,
+ [
+ Provider1::default(),
+ Provider2::default(),
+ Provider3::default(),
+ ]
+ );
+ }
+}
diff --git a/vendor/icu_provider_adapters/src/fork/mod.rs b/vendor/icu_provider_adapters/src/fork/mod.rs
new file mode 100644
index 000000000..05ba5fd19
--- /dev/null
+++ b/vendor/icu_provider_adapters/src/fork/mod.rs
@@ -0,0 +1,226 @@
+// 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 ).
+
+//! Providers that combine multiple other providers.
+//!
+//! # Types of Forking Providers
+//!
+//! ## Key-Based
+//!
+//! To fork between providers that support different data keys, see:
+//!
+//! - [`ForkByKeyProvider`]
+//! - [`MultiForkByKeyProvider`]
+//!
+//! ## Locale-Based
+//!
+//! To fork between providers that support different locales, see:
+//!
+//! - [`ForkByErrorProvider`]`<`[`MissingLocalePredicate`]`>`
+//! - [`MultiForkByErrorProvider`]`<`[`MissingLocalePredicate`]`>`
+//!
+//! [`MissingLocalePredicate`]: predicates::MissingLocalePredicate
+//!
+//! # Examples
+//!
+//! See:
+//!
+//! - [`ForkByKeyProvider`]
+//! - [`MultiForkByKeyProvider`]
+//! - [`MissingLocalePredicate`]
+
+use alloc::vec::Vec;
+
+mod by_error;
+
+pub mod predicates;
+
+#[macro_use]
+mod macros;
+
+pub use by_error::ForkByErrorProvider;
+pub use by_error::MultiForkByErrorProvider;
+
+use predicates::ForkByErrorPredicate;
+use predicates::MissingDataKeyPredicate;
+
+/// Create a provider that returns data from one of two child providers based on the key.
+///
+/// The result of the first provider that supports a particular [`DataKey`] will be returned,
+/// even if the request failed for other reasons (such as an unsupported language). Therefore,
+/// you should add child providers that support disjoint sets of keys.
+///
+/// [`ForkByKeyProvider`] does not support forking between [`DataProvider`]s. However, it
+/// supports forking between [`AnyProvider`], [`BufferProvider`], and [`DynamicDataProvider`].
+///
+/// # Examples
+///
+/// Normal usage:
+///
+/// ```
+/// use icu_locid::locale;
+/// use icu_provider::hello_world::*;
+/// use icu_provider::prelude::*;
+/// use icu_provider_adapters::fork::ForkByKeyProvider;
+///
+/// struct DummyBufferProvider;
+/// impl BufferProvider for DummyBufferProvider {
+/// fn load_buffer(
+/// &self,
+/// key: DataKey,
+/// req: DataRequest,
+/// ) -> Result<DataResponse<BufferMarker>, DataError> {
+/// Err(DataErrorKind::MissingDataKey.with_req(key, req))
+/// }
+/// }
+///
+/// let forking_provider = ForkByKeyProvider::new(
+/// DummyBufferProvider,
+/// HelloWorldProvider.into_json_provider(),
+/// );
+///
+/// let data_provider = forking_provider.as_deserializing();
+///
+/// let german_hello_world: DataPayload<HelloWorldV1Marker> = data_provider
+/// .load(DataRequest {
+/// locale: &locale!("de").into(),
+/// metadata: Default::default(),
+/// })
+/// .expect("Loading should succeed")
+/// .take_payload()
+/// .expect("Data should be present");
+///
+/// assert_eq!("Hallo Welt", german_hello_world.get().message);
+/// ```
+///
+/// Stops at the first provider supporting a key, even if the locale is not supported:
+///
+/// ```
+/// use icu_locid::{subtags_language as language, locale};
+/// use icu_provider::hello_world::*;
+/// use icu_provider::prelude::*;
+/// use icu_provider_adapters::filter::Filterable;
+/// use icu_provider_adapters::fork::ForkByKeyProvider;
+///
+/// let forking_provider = ForkByKeyProvider::new(
+/// HelloWorldProvider
+/// .into_json_provider()
+/// .filterable("Chinese")
+/// .filter_by_langid(|langid| langid.language == language!("zh")),
+/// HelloWorldProvider
+/// .into_json_provider()
+/// .filterable("German")
+/// .filter_by_langid(|langid| langid.language == language!("de")),
+/// );
+///
+/// let data_provider: &dyn DataProvider<HelloWorldV1Marker> =
+/// &forking_provider.as_deserializing();
+///
+/// // Chinese is the first provider, so this succeeds
+/// let chinese_hello_world = data_provider
+/// .load(DataRequest {
+/// locale: &locale!("zh").into(),
+/// metadata: Default::default(),
+/// })
+/// .expect("Loading should succeed")
+/// .take_payload()
+/// .expect("Data should be present");
+///
+/// assert_eq!("你好世界", chinese_hello_world.get().message);
+///
+/// // German is shadowed by Chinese, so this fails
+/// data_provider
+/// .load(DataRequest {
+/// locale: &locale!("de").into(),
+/// metadata: Default::default(),
+/// })
+/// .expect_err("Should stop at the first provider, even though the second has data");
+/// ```
+///
+/// [`DataKey`]: icu_provider::DataKey
+/// [`DataProvider`]: icu_provider::DataProvider
+/// [`AnyProvider`]: icu_provider::AnyProvider
+/// [`BufferProvider`]: icu_provider::BufferProvider
+/// [`DynamicDataProvider`]: icu_provider::DynamicDataProvider
+pub type ForkByKeyProvider<P0, P1> = ForkByErrorProvider<P0, P1, MissingDataKeyPredicate>;
+
+impl<P0, P1> ForkByKeyProvider<P0, P1> {
+ /// A provider that returns data from one of two child providers based on the key.
+ ///
+ /// See [`ForkByKeyProvider`].
+ pub fn new(p0: P0, p1: P1) -> Self {
+ ForkByErrorProvider::new_with_predicate(p0, p1, MissingDataKeyPredicate)
+ }
+}
+
+/// A provider that returns data from the first child provider supporting the key.
+///
+/// The result of the first provider that supports a particular [`DataKey`] will be returned,
+/// even if the request failed for other reasons (such as an unsupported language). Therefore,
+/// you should add child providers that support disjoint sets of keys.
+///
+/// [`MultiForkByKeyProvider`] does not support forking between [`DataProvider`]s. However, it
+/// supports forking between [`AnyProvider`], [`BufferProvider`], and [`DynamicDataProvider`].
+///
+/// # Examples
+///
+/// ```
+/// use icu_locid::{subtags_language as language, locale};
+/// use icu_provider::hello_world::*;
+/// use icu_provider::prelude::*;
+/// use icu_provider_adapters::filter::Filterable;
+/// use icu_provider_adapters::fork::MultiForkByKeyProvider;
+///
+/// let forking_provider = MultiForkByKeyProvider::new(
+/// vec![
+/// HelloWorldProvider
+/// .into_json_provider()
+/// .filterable("Chinese")
+/// .filter_by_langid(|langid| langid.language == language!("zh")),
+/// HelloWorldProvider
+/// .into_json_provider()
+/// .filterable("German")
+/// .filter_by_langid(|langid| langid.language == language!("de")),
+/// ],
+/// );
+///
+/// let data_provider: &dyn DataProvider<HelloWorldV1Marker> =
+/// &forking_provider.as_deserializing();
+///
+/// // Chinese is the first provider, so this succeeds
+/// let chinese_hello_world = data_provider
+/// .load(DataRequest {
+/// locale: &locale!("zh").into(),
+/// metadata: Default::default(),
+/// })
+/// .expect("Loading should succeed")
+/// .take_payload()
+/// .expect("Data should be present");
+///
+/// assert_eq!("你好世界", chinese_hello_world.get().message);
+///
+/// // German is shadowed by Chinese, so this fails
+/// data_provider
+/// .load(DataRequest {
+/// locale: &locale!("de").into(),
+/// metadata: Default::default(),
+/// })
+/// .expect_err("Should stop at the first provider, even though the second has data");
+/// ```
+///
+/// [`DataKey`]: icu_provider::DataKey
+/// [`DataProvider`]: icu_provider::DataProvider
+/// [`AnyProvider`]: icu_provider::AnyProvider
+/// [`BufferProvider`]: icu_provider::BufferProvider
+/// [`DynamicDataProvider`]: icu_provider::DynamicDataProvider
+pub type MultiForkByKeyProvider<P> = MultiForkByErrorProvider<P, MissingDataKeyPredicate>;
+
+impl<P> MultiForkByKeyProvider<P> {
+ /// Create a provider that returns data from the first child provider supporting the key.
+ ///
+ /// See [`MultiForkByKeyProvider`].
+ pub fn new(providers: Vec<P>) -> Self {
+ MultiForkByErrorProvider::new_with_predicate(providers, MissingDataKeyPredicate)
+ }
+}
diff --git a/vendor/icu_provider_adapters/src/fork/predicates.rs b/vendor/icu_provider_adapters/src/fork/predicates.rs
new file mode 100644
index 000000000..0fefe5704
--- /dev/null
+++ b/vendor/icu_provider_adapters/src/fork/predicates.rs
@@ -0,0 +1,138 @@
+// 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 ).
+
+//! Collection of predicate traits and functions for forking providers.
+
+use icu_provider::prelude::*;
+
+/// The predicate trait used by [`ForkByErrorProvider`].
+///
+/// [`ForkByErrorProvider`]: super::ForkByErrorProvider
+pub trait ForkByErrorPredicate {
+ /// This function is called when a data request fails and there are additional providers
+ /// that could possibly fulfill the request.
+ ///
+ /// Arguments:
+ ///
+ /// - `&self` = Reference to the struct implementing the trait (for data capture)
+ /// - `key` = The [`DataKey`] associated with the request
+ /// - `req` = The [`DataRequest`]. This may be `None` if there is no request, such as
+ /// inside [`IterableDynamicDataProvider`].
+ /// - `err` = The error that occurred.
+ ///
+ /// Return value:
+ ///
+ /// - `true` to discard the error and attempt the request with the next provider.
+ /// - `false` to return the error and not perform any additional requests.
+ ///
+ /// [`DataKey`]: icu_provider::DataKey
+ /// [`DataRequest`]: icu_provider::DataRequest
+ /// [`IterableDynamicDataProvider`]: icu_provider::datagen::IterableDynamicDataProvider
+ fn test(&self, key: DataKey, req: Option<DataRequest>, err: DataError) -> bool;
+}
+
+/// A predicate that allows forking providers to search for a provider that supports a
+/// particular data key.
+///
+/// This is normally used implicitly by [`ForkByKeyProvider`].
+///
+/// [`ForkByKeyProvider`]: super::ForkByKeyProvider
+#[derive(Debug, PartialEq, Eq)]
+#[non_exhaustive] // Not intended to be constructed
+pub struct MissingDataKeyPredicate;
+
+impl ForkByErrorPredicate for MissingDataKeyPredicate {
+ #[inline]
+ fn test(&self, _: DataKey, _: Option<DataRequest>, err: DataError) -> bool {
+ matches!(
+ err,
+ DataError {
+ kind: DataErrorKind::MissingDataKey,
+ ..
+ }
+ )
+ }
+}
+
+/// A predicate that allows forking providers to search for a provider that supports a
+/// particular locale, based on whether it returns [`DataErrorKind::MissingLocale`].
+///
+/// # Examples
+///
+/// Configure a multi-language data provider pointing at two language packs:
+///
+/// ```
+/// use icu_provider_adapters::fork::ForkByErrorProvider;
+/// use icu_provider_adapters::fork::predicates::MissingLocalePredicate;
+/// use icu_provider_fs::FsDataProvider;
+/// use icu_provider::prelude::*;
+/// use icu_provider::hello_world::HelloWorldV1Marker;
+/// use icu_locid::locale;
+///
+/// // The `tests` directory contains two separate "language packs" for Hello World data.
+/// let base_dir = std::path::PathBuf::from(std::env!("CARGO_MANIFEST_DIR"))
+/// .join("tests/data/langtest");
+/// let provider_de = FsDataProvider::try_new(base_dir.join("de")).unwrap();
+/// let provider_ro = FsDataProvider::try_new(base_dir.join("ro")).unwrap();
+///
+/// // Create the forking provider:
+/// let provider = ForkByErrorProvider::new_with_predicate(
+/// provider_de,
+/// provider_ro,
+/// MissingLocalePredicate
+/// );
+///
+/// // Test that we can load both "de" and "ro" data:
+///
+/// let german_hello_world: DataPayload<HelloWorldV1Marker> = provider
+/// .as_deserializing()
+/// .load(DataRequest {
+/// locale: &locale!("de").into(),
+/// metadata: Default::default(),
+/// })
+/// .expect("Loading should succeed")
+/// .take_payload()
+/// .expect("Data should be present");
+///
+/// assert_eq!("Hallo Welt", german_hello_world.get().message);
+///
+/// let romanian_hello_world: DataPayload<HelloWorldV1Marker> = provider
+/// .as_deserializing()
+/// .load(DataRequest {
+/// locale: &locale!("ro").into(),
+/// metadata: Default::default(),
+/// })
+/// .expect("Loading should succeed")
+/// .take_payload()
+/// .expect("Data should be present");
+///
+/// assert_eq!("Salut, lume", romanian_hello_world.get().message);
+///
+/// // We should not be able to load "en" data because it is not in the provider:
+///
+/// DataProvider::<HelloWorldV1Marker>::load(
+/// &provider.as_deserializing(),
+/// DataRequest {
+/// locale: &locale!("en").into(),
+/// metadata: Default::default(),
+/// }
+/// )
+/// .expect_err("No English data");
+/// ```
+#[derive(Debug, PartialEq, Eq)]
+#[allow(clippy::exhaustive_structs)] // empty type
+pub struct MissingLocalePredicate;
+
+impl ForkByErrorPredicate for MissingLocalePredicate {
+ #[inline]
+ fn test(&self, _: DataKey, _: Option<DataRequest>, err: DataError) -> bool {
+ matches!(
+ err,
+ DataError {
+ kind: DataErrorKind::MissingLocale,
+ ..
+ }
+ )
+ }
+}
diff --git a/vendor/icu_provider_adapters/src/helpers.rs b/vendor/icu_provider_adapters/src/helpers.rs
new file mode 100644
index 000000000..2b168f936
--- /dev/null
+++ b/vendor/icu_provider_adapters/src/helpers.rs
@@ -0,0 +1,15 @@
+// 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 icu_provider::prelude::*;
+
+pub(crate) fn result_is_err_missing_data_options<T>(result: &Result<T, DataError>) -> bool {
+ matches!(
+ result,
+ Err(DataError {
+ kind: DataErrorKind::MissingLocale,
+ ..
+ })
+ )
+}
diff --git a/vendor/icu_provider_adapters/src/lib.rs b/vendor/icu_provider_adapters/src/lib.rs
new file mode 100644
index 000000000..16361e891
--- /dev/null
+++ b/vendor/icu_provider_adapters/src/lib.rs
@@ -0,0 +1,36 @@
+// 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 ).
+
+//! Adapters for composing and manipulating data providers.
+//!
+//! - Use the [`fork`] module to marshall data requests between multiple possible providers.
+//! - Use the [`either`] module to choose between multiple provider types at runtime.
+//! - Use the [`filter`] module to programmatically reject certain data requests.
+//! - Use the [`fallback`] module to automatically resolve arbitrary locales for data loading.
+
+// https://github.com/unicode-org/icu4x/blob/main/docs/process/boilerplate.md#library-annotations
+#![cfg_attr(not(any(test, feature = "std")), no_std)]
+#![cfg_attr(
+ not(test),
+ deny(
+ clippy::indexing_slicing,
+ clippy::unwrap_used,
+ clippy::expect_used,
+ clippy::panic,
+ clippy::exhaustive_structs,
+ clippy::exhaustive_enums,
+ // TODO(#2266): enable missing_debug_implementations,
+ )
+)]
+#![warn(missing_docs)]
+
+extern crate alloc;
+
+pub mod any_payload;
+pub mod either;
+pub mod empty;
+pub mod fallback;
+pub mod filter;
+pub mod fork;
+mod helpers;
diff --git a/vendor/icu_provider_adapters/tests/data/langtest/de/core/helloworld@1/de.json b/vendor/icu_provider_adapters/tests/data/langtest/de/core/helloworld@1/de.json
new file mode 100644
index 000000000..82d456e8e
--- /dev/null
+++ b/vendor/icu_provider_adapters/tests/data/langtest/de/core/helloworld@1/de.json
@@ -0,0 +1 @@
+{"message":"Hallo Welt"}
diff --git a/vendor/icu_provider_adapters/tests/data/langtest/de/manifest.json b/vendor/icu_provider_adapters/tests/data/langtest/de/manifest.json
new file mode 100644
index 000000000..847813772
--- /dev/null
+++ b/vendor/icu_provider_adapters/tests/data/langtest/de/manifest.json
@@ -0,0 +1,3 @@
+{
+ "syntax": "Json"
+}
diff --git a/vendor/icu_provider_adapters/tests/data/langtest/ro/core/helloworld@1/ro.json b/vendor/icu_provider_adapters/tests/data/langtest/ro/core/helloworld@1/ro.json
new file mode 100644
index 000000000..a9b3e8dcf
--- /dev/null
+++ b/vendor/icu_provider_adapters/tests/data/langtest/ro/core/helloworld@1/ro.json
@@ -0,0 +1 @@
+{"message":"Salut, lume"}
diff --git a/vendor/icu_provider_adapters/tests/data/langtest/ro/manifest.json b/vendor/icu_provider_adapters/tests/data/langtest/ro/manifest.json
new file mode 100644
index 000000000..847813772
--- /dev/null
+++ b/vendor/icu_provider_adapters/tests/data/langtest/ro/manifest.json
@@ -0,0 +1,3 @@
+{
+ "syntax": "Json"
+}