1// This file is part of ICU4X. For terms of use, please see the file
2// called LICENSE at the top level of the ICU4X source tree
3// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
4
5//! Collection of predicate traits and functions for forking providers.
6
7use icu_provider::prelude::*;
8
9/// The predicate trait used by [`ForkByErrorProvider`].
10///
11/// [`ForkByErrorProvider`]: super::ForkByErrorProvider
12pub trait ForkByErrorPredicate {
13 /// The error to return if there are zero providers.
14 const UNIT_ERROR: DataErrorKind = DataErrorKind::MissingDataKey;
15
16 /// This function is called when a data request fails and there are additional providers
17 /// that could possibly fulfill the request.
18 ///
19 /// Arguments:
20 ///
21 /// - `&self` = Reference to the struct implementing the trait (for data capture)
22 /// - `key` = The [`DataKey`] associated with the request
23 /// - `req` = The [`DataRequest`]. This may be `None` if there is no request, such as
24 /// inside [`IterableDynamicDataProvider`].
25 /// - `err` = The error that occurred.
26 ///
27 /// Return value:
28 ///
29 /// - `true` to discard the error and attempt the request with the next provider.
30 /// - `false` to return the error and not perform any additional requests.
31 ///
32 /// [`DataKey`]: icu_provider::DataKey
33 /// [`DataRequest`]: icu_provider::DataRequest
34 /// [`IterableDynamicDataProvider`]: icu_provider::datagen::IterableDynamicDataProvider
35 fn test(&self, key: DataKey, req: Option<DataRequest>, err: DataError) -> bool;
36}
37
38/// A predicate that allows forking providers to search for a provider that supports a
39/// particular data key.
40///
41/// This is normally used implicitly by [`ForkByKeyProvider`].
42///
43/// [`ForkByKeyProvider`]: super::ForkByKeyProvider
44#[derive(Debug, PartialEq, Eq)]
45#[non_exhaustive] // Not intended to be constructed
46pub struct MissingDataKeyPredicate;
47
48impl ForkByErrorPredicate for MissingDataKeyPredicate {
49 const UNIT_ERROR: DataErrorKind = DataErrorKind::MissingDataKey;
50
51 #[inline]
52 fn test(&self, _: DataKey, _: Option<DataRequest>, err: DataError) -> bool {
53 matches!(
54 err,
55 DataError {
56 kind: DataErrorKind::MissingDataKey,
57 ..
58 }
59 )
60 }
61}
62
63/// A predicate that allows forking providers to search for a provider that supports a
64/// particular locale, based on whether it returns [`DataErrorKind::MissingLocale`].
65///
66/// # Examples
67///
68/// Configure a multi-language data provider pointing at two language packs:
69///
70/// ```
71/// use icu_provider_adapters::fork::ForkByErrorProvider;
72/// use icu_provider_adapters::fork::predicates::MissingLocalePredicate;
73/// use icu_provider_fs::FsDataProvider;
74/// use icu_provider::prelude::*;
75/// use icu_provider::hello_world::HelloWorldV1Marker;
76/// use icu_locid::locale;
77///
78/// // The `tests` directory contains two separate "language packs" for Hello World data.
79/// let base_dir = std::path::PathBuf::from(std::env!("CARGO_MANIFEST_DIR"))
80/// .join("tests/data/langtest");
81/// let provider_de = FsDataProvider::try_new(base_dir.join("de")).unwrap();
82/// let provider_ro = FsDataProvider::try_new(base_dir.join("ro")).unwrap();
83///
84/// // Create the forking provider:
85/// let provider = ForkByErrorProvider::new_with_predicate(
86/// provider_de,
87/// provider_ro,
88/// MissingLocalePredicate
89/// );
90///
91/// // Test that we can load both "de" and "ro" data:
92///
93/// let german_hello_world: DataPayload<HelloWorldV1Marker> = provider
94/// .as_deserializing()
95/// .load(DataRequest {
96/// locale: &locale!("de").into(),
97/// metadata: Default::default(),
98/// })
99/// .expect("Loading should succeed")
100/// .take_payload()
101/// .expect("Data should be present");
102///
103/// assert_eq!("Hallo Welt", german_hello_world.get().message);
104///
105/// let romanian_hello_world: DataPayload<HelloWorldV1Marker> = provider
106/// .as_deserializing()
107/// .load(DataRequest {
108/// locale: &locale!("ro").into(),
109/// metadata: Default::default(),
110/// })
111/// .expect("Loading should succeed")
112/// .take_payload()
113/// .expect("Data should be present");
114///
115/// assert_eq!("Salut, lume", romanian_hello_world.get().message);
116///
117/// // We should not be able to load "en" data because it is not in the provider:
118///
119/// DataProvider::<HelloWorldV1Marker>::load(
120/// &provider.as_deserializing(),
121/// DataRequest {
122/// locale: &locale!("en").into(),
123/// metadata: Default::default(),
124/// }
125/// )
126/// .expect_err("No English data");
127/// ```
128#[derive(Debug, PartialEq, Eq)]
129#[allow(clippy::exhaustive_structs)] // empty type
130pub struct MissingLocalePredicate;
131
132impl ForkByErrorPredicate for MissingLocalePredicate {
133 const UNIT_ERROR: DataErrorKind = DataErrorKind::MissingLocale;
134
135 #[inline]
136 fn test(&self, _: DataKey, _: Option<DataRequest>, err: DataError) -> bool {
137 matches!(
138 err,
139 DataError {
140 kind: DataErrorKind::MissingLocale,
141 ..
142 }
143 )
144 }
145}
146