summaryrefslogtreecommitdiffstats
path: root/third_party/rust/icu_provider_adapters/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/rust/icu_provider_adapters/src
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/icu_provider_adapters/src')
-rw-r--r--third_party/rust/icu_provider_adapters/src/any_payload.rs124
-rw-r--r--third_party/rust/icu_provider_adapters/src/either.rs112
-rw-r--r--third_party/rust/icu_provider_adapters/src/empty.rs112
-rw-r--r--third_party/rust/icu_provider_adapters/src/fallback/mod.rs298
-rw-r--r--third_party/rust/icu_provider_adapters/src/filter/impls.rs216
-rw-r--r--third_party/rust/icu_provider_adapters/src/filter/mod.rs243
-rw-r--r--third_party/rust/icu_provider_adapters/src/fork/by_error.rs309
-rw-r--r--third_party/rust/icu_provider_adapters/src/fork/macros.rs60
-rw-r--r--third_party/rust/icu_provider_adapters/src/fork/mod.rs226
-rw-r--r--third_party/rust/icu_provider_adapters/src/fork/predicates.rs143
-rw-r--r--third_party/rust/icu_provider_adapters/src/helpers.rs15
-rw-r--r--third_party/rust/icu_provider_adapters/src/lib.rs36
12 files changed, 1894 insertions, 0 deletions
diff --git a/third_party/rust/icu_provider_adapters/src/any_payload.rs b/third_party/rust/icu_provider_adapters/src/any_payload.rs
new file mode 100644
index 0000000000..3179a1de8e
--- /dev/null
+++ b/third_party/rust/icu_provider_adapters/src/any_payload.rs
@@ -0,0 +1,124 @@
+// 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,
+/// ..
+/// })
+/// ))
+/// ```
+#[derive(Debug)]
+#[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>(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>(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>(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>() -> 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,
+ 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/third_party/rust/icu_provider_adapters/src/either.rs b/third_party/rust/icu_provider_adapters/src/either.rs
new file mode 100644
index 0000000000..5927795ef9
--- /dev/null
+++ b/third_party/rust/icu_provider_adapters/src/either.rs
@@ -0,0 +1,112 @@
+// 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
+#[derive(Debug)]
+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/third_party/rust/icu_provider_adapters/src/empty.rs b/third_party/rust/icu_provider_adapters/src/empty.rs
new file mode 100644
index 0000000000..e8592bc253
--- /dev/null
+++ b/third_party/rust/icu_provider_adapters/src/empty.rs
@@ -0,0 +1,112 @@
+// 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,
+/// ..
+/// })
+/// ));
+/// ```
+#[derive(Debug)]
+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))
+ }
+}
+
+#[cfg(feature = "datagen")]
+impl<M> icu_provider::datagen::IterableDataProvider<M> for EmptyDataProvider
+where
+ M: KeyedDataMarker,
+{
+ fn supported_locales(&self) -> Result<alloc::vec::Vec<DataLocale>, DataError> {
+ Ok(vec![])
+ }
+}
+
+#[cfg(feature = "datagen")]
+impl<M> icu_provider::datagen::IterableDynamicDataProvider<M> for EmptyDataProvider
+where
+ M: DataMarker,
+{
+ fn supported_locales_for_key(
+ &self,
+ _: DataKey,
+ ) -> Result<alloc::vec::Vec<DataLocale>, DataError> {
+ Ok(vec![])
+ }
+}
diff --git a/third_party/rust/icu_provider_adapters/src/fallback/mod.rs b/third_party/rust/icu_provider_adapters/src/fallback/mod.rs
new file mode 100644
index 0000000000..c0a05d14ec
--- /dev/null
+++ b/third_party/rust/icu_provider_adapters/src/fallback/mod.rs
@@ -0,0 +1,298 @@
+// 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 ).
+
+//! A data provider wrapper that performs locale fallback.
+
+use crate::helpers::result_is_err_missing_locale;
+use icu_locid_transform::provider::*;
+use icu_provider::prelude::*;
+
+#[doc(hidden)] // moved
+pub use icu_locid_transform::fallback::{
+ LocaleFallbackIterator, LocaleFallbacker, LocaleFallbackerWithConfig,
+};
+#[doc(hidden)] // moved
+pub use icu_provider::fallback::LocaleFallbackConfig;
+
+/// 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_provider_blob::BlobDataProvider::try_new_from_static_blob(include_bytes!("../../tests/data/blob.postcard")).unwrap();
+/// # let provider = provider.as_deserializing();
+///
+/// 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!(
+/// response.metadata.locale.unwrap(),
+/// locale!("ja").into(),
+/// );
+/// assert_eq!(
+/// response.payload.unwrap().get().message,
+/// "こんにちは世界",
+/// );
+/// ```
+#[derive(Clone, Debug)]
+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_locid_transform::LocaleFallbacker;
+ /// use icu_provider::hello_world::*;
+ /// use icu_provider::prelude::*;
+ /// use icu_provider_adapters::fallback::LocaleFallbackProvider;
+ ///
+ /// 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 construct a fallbacker with `icu_locid_transform`'s
+ /// // compiled data.
+ /// let provider = LocaleFallbackProvider::new_with_fallbacker(
+ /// provider,
+ /// LocaleFallbacker::new().static_to_owned(),
+ /// );
+ ///
+ /// // 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 a mutable reference to the inner provider.
+ pub fn inner_mut(&mut self) -> &mut P {
+ &mut 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,
+ mut 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,
+ {
+ if key.metadata().singleton {
+ return f1(base_req);
+ }
+ let mut fallback_iterator = self
+ .fallbacker
+ .for_config(key.fallback_config())
+ .fallback_for(base_req.locale.clone());
+ let base_silent = core::mem::replace(&mut base_req.metadata.silent, true);
+ loop {
+ let result = f1(DataRequest {
+ locale: fallback_iterator.get(),
+ metadata: base_req.metadata,
+ });
+ if !result_is_err_missing_locale(&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| {
+ base_req.metadata.silent = base_silent;
+ e.with_req(key, base_req)
+ });
+ }
+ // If we just checked und, break out of the loop.
+ if fallback_iterator.get().is_und() {
+ break;
+ }
+ fallback_iterator.step();
+ }
+ base_req.metadata.silent = base_silent;
+ 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/third_party/rust/icu_provider_adapters/src/filter/impls.rs b/third_party/rust/icu_provider_adapters/src/filter/impls.rs
new file mode 100644
index 0000000000..f24248effa
--- /dev/null
+++ b/third_party/rust/icu_provider_adapters/src/filter/impls.rs
@@ -0,0 +1,216 @@
+// 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;
+
+type RequestFilterDataProviderOutput<'a, D> =
+ RequestFilterDataProvider<D, Box<dyn Fn(DataRequest) -> bool + Sync + 'a>>;
+
+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};
+ /// 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,
+ ) -> RequestFilterDataProviderOutput<'a, D>
+ 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 = [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],
+ ) -> RequestFilterDataProviderOutput<'a, D>
+ 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) -> RequestFilterDataProviderOutput<'a, D>
+ 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/third_party/rust/icu_provider_adapters/src/filter/mod.rs b/third_party/rust/icu_provider_adapters/src/filter/mod.rs
new file mode 100644
index 0000000000..19b6bf2335
--- /dev/null
+++ b/third_party/rust/icu_provider_adapters/src/filter/mod.rs
@@ -0,0 +1,243 @@
+// 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;
+//! 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
+#[derive(Debug)]
+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/third_party/rust/icu_provider_adapters/src/fork/by_error.rs b/third_party/rust/icu_provider_adapters/src/fork/by_error.rs
new file mode 100644
index 0000000000..5d25ce2072
--- /dev/null
+++ b/third_party/rust/icu_provider_adapters/src/fork/by_error.rs
@@ -0,0 +1,309 @@
+// 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 mutable references to the inner providers.
+ pub fn inner_mut(&mut self) -> (&mut P0, &mut P1) {
+ (&mut self.0, &mut 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`].
+#[derive(Debug)]
+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
+ }
+
+ /// Exposes a mutable vector of providers to a closure so it can be mutated.
+ pub fn with_inner_mut(&mut self, f: impl FnOnce(&mut Vec<P>)) {
+ f(&mut self.providers)
+ }
+
+ /// Returns ownership of the inner providers to the caller.
+ pub fn into_inner(self) -> Vec<P> {
+ self.providers
+ }
+
+ /// Adds an additional child provider.
+ pub fn push(&mut self, provider: P) {
+ self.providers.push(provider);
+ }
+}
+
+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> {
+ let mut last_error = F::UNIT_ERROR.with_key(key);
+ 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(err) => last_error = err,
+ };
+ }
+ Err(last_error)
+ }
+}
+
+impl<P, F> AnyProvider for MultiForkByErrorProvider<P, F>
+where
+ P: AnyProvider,
+ F: ForkByErrorPredicate,
+{
+ fn load_any(&self, key: DataKey, req: DataRequest) -> Result<AnyResponse, DataError> {
+ let mut last_error = F::UNIT_ERROR.with_key(key);
+ 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(err) => last_error = err,
+ };
+ }
+ Err(last_error)
+ }
+}
+
+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> {
+ let mut last_error = F::UNIT_ERROR.with_key(key);
+ 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(err) => last_error = err,
+ };
+ }
+ Err(last_error)
+ }
+}
+
+#[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> {
+ let mut last_error = F::UNIT_ERROR.with_key(key);
+ 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(err) => last_error = err,
+ };
+ }
+ Err(last_error)
+ }
+}
+
+#[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)> {
+ let mut last_error = F::UNIT_ERROR.with_key(key);
+ 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;
+ last_error = err;
+ }
+ };
+ }
+ Err((from, last_error))
+ }
+}
+
+#[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/third_party/rust/icu_provider_adapters/src/fork/macros.rs b/third_party/rust/icu_provider_adapters/src/fork/macros.rs
new file mode 100644
index 0000000000..eb98ec693a
--- /dev/null
+++ b/third_party/rust/icu_provider_adapters/src/fork/macros.rs
@@ -0,0 +1,60 @@
+// 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(PartialEq, Debug)]
+/// struct Provider1;
+/// #[derive(PartialEq, Debug)]
+/// struct Provider2;
+/// #[derive(PartialEq, Debug)]
+/// struct Provider3;
+///
+/// // Combine them into one:
+/// let forking1 = icu_provider_adapters::make_forking_provider!(
+/// ForkByKeyProvider::new,
+/// [Provider1, Provider2, Provider3,]
+/// );
+///
+/// // This is equivalent to:
+/// let forking2 = ForkByKeyProvider::new(
+/// Provider1,
+/// ForkByKeyProvider::new(Provider2, Provider3),
+/// );
+///
+/// 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 {
+ struct Provider1;
+ struct Provider2;
+ struct Provider3;
+
+ #[test]
+ fn test_make_forking_provider() {
+ make_forking_provider!(
+ crate::fork::ForkByKeyProvider::new,
+ [Provider1, Provider2, Provider3,]
+ );
+ }
+}
diff --git a/third_party/rust/icu_provider_adapters/src/fork/mod.rs b/third_party/rust/icu_provider_adapters/src/fork/mod.rs
new file mode 100644
index 0000000000..a15c742c60
--- /dev/null
+++ b/third_party/rust/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 provider = forking_provider.as_deserializing();
+///
+/// let german_hello_world: DataPayload<HelloWorldV1Marker> = 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, 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 provider: &dyn DataProvider<HelloWorldV1Marker> =
+/// &forking_provider.as_deserializing();
+///
+/// // Chinese is the first provider, so this succeeds
+/// let chinese_hello_world = 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
+/// 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, 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 provider: &dyn DataProvider<HelloWorldV1Marker> =
+/// &forking_provider.as_deserializing();
+///
+/// // Chinese is the first provider, so this succeeds
+/// let chinese_hello_world = 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
+/// 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/third_party/rust/icu_provider_adapters/src/fork/predicates.rs b/third_party/rust/icu_provider_adapters/src/fork/predicates.rs
new file mode 100644
index 0000000000..d1e86529ff
--- /dev/null
+++ b/third_party/rust/icu_provider_adapters/src/fork/predicates.rs
@@ -0,0 +1,143 @@
+// 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 {
+ /// The error to return if there are zero providers.
+ const UNIT_ERROR: DataErrorKind = DataErrorKind::MissingDataKey;
+
+ /// 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 {
+ const UNIT_ERROR: DataErrorKind = DataErrorKind::MissingDataKey;
+
+ #[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 provider_de = FsDataProvider::try_new("tests/data/langtest/de").unwrap();
+/// let provider_ro = FsDataProvider::try_new("tests/data/langtest/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 {
+ const UNIT_ERROR: DataErrorKind = DataErrorKind::MissingLocale;
+
+ #[inline]
+ fn test(&self, _: DataKey, _: Option<DataRequest>, err: DataError) -> bool {
+ matches!(
+ err,
+ DataError {
+ kind: DataErrorKind::MissingLocale,
+ ..
+ }
+ )
+ }
+}
diff --git a/third_party/rust/icu_provider_adapters/src/helpers.rs b/third_party/rust/icu_provider_adapters/src/helpers.rs
new file mode 100644
index 0000000000..bddab00116
--- /dev/null
+++ b/third_party/rust/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_locale<T>(result: &Result<T, DataError>) -> bool {
+ matches!(
+ result,
+ Err(DataError {
+ kind: DataErrorKind::MissingLocale,
+ ..
+ })
+ )
+}
diff --git a/third_party/rust/icu_provider_adapters/src/lib.rs b/third_party/rust/icu_provider_adapters/src/lib.rs
new file mode 100644
index 0000000000..81987f8778
--- /dev/null
+++ b/third_party/rust/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,
+ 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;