icu_provider_adapters/fork/predicates.rs
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::MarkerNotFound;
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 /// - `marker` = The [`DataMarkerInfo`] 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 fn test(&self, marker: DataMarkerInfo, req: Option<DataRequest>, err: DataError) -> bool;
32}
33
34/// A predicate that allows forking providers to search for a provider that supports a
35/// particular data marker.
36///
37/// This is normally used implicitly by [`ForkByMarkerProvider`].
38///
39/// [`ForkByMarkerProvider`]: super::ForkByMarkerProvider
40#[derive(Debug, PartialEq, Eq)]
41#[non_exhaustive] // Not intended to be constructed
42pub struct MarkerNotFoundPredicate;
43
44impl ForkByErrorPredicate for MarkerNotFoundPredicate {
45 const UNIT_ERROR: DataErrorKind = DataErrorKind::MarkerNotFound;
46
47 #[inline]
48 fn test(&self, _: DataMarkerInfo, _: Option<DataRequest>, err: DataError) -> bool {
49 matches!(
50 err,
51 DataError {
52 kind: DataErrorKind::MarkerNotFound,
53 ..
54 }
55 )
56 }
57}
58
59/// A predicate that allows forking providers to search for a provider that supports a
60/// particular locale, based on whether it returns [`DataErrorKind::IdentifierNotFound`].
61///
62/// # Examples
63///
64/// ```
65/// use icu_provider_adapters::fork::ForkByErrorProvider;
66/// use icu_provider_adapters::fork::predicates::IdentifierNotFoundPredicate;
67/// use icu_provider::prelude::*;
68/// use icu_provider::hello_world::*;
69/// use icu_locale::langid;
70///
71/// struct SingleLocaleProvider(DataLocale);
72/// impl DataProvider<HelloWorldV1> for SingleLocaleProvider {
73/// fn load(&self, req: DataRequest) -> Result<DataResponse<HelloWorldV1>, DataError> {
74/// if *req.id.locale != self.0 {
75/// return Err(DataErrorKind::IdentifierNotFound.with_req(HelloWorldV1::INFO, req));
76/// }
77/// HelloWorldProvider.load(req)
78/// }
79/// }
80///
81/// let provider_de = SingleLocaleProvider(langid!("de").into());
82/// let provider_ro = SingleLocaleProvider(langid!("ro").into());
83///
84/// // Create the forking provider:
85/// let provider = ForkByErrorProvider::new_with_predicate(
86/// provider_de,
87/// provider_ro,
88/// IdentifierNotFoundPredicate
89/// );
90///
91/// // Test that we can load both "de" and "ro" data:
92///
93/// let german_hello_world: DataResponse<HelloWorldV1> = provider
94/// .load(DataRequest {
95/// id: DataIdentifierBorrowed::for_locale(&langid!("de").into()),
96/// ..Default::default()
97/// })
98/// .expect("Loading should succeed");
99///
100/// assert_eq!("Hallo Welt", german_hello_world.payload.get().message);
101///
102/// let romanian_hello_world: DataResponse<HelloWorldV1> = provider
103/// .load(DataRequest {
104/// id: DataIdentifierBorrowed::for_locale(&langid!("ro").into()),
105/// ..Default::default()
106/// })
107/// .expect("Loading should succeed");
108///
109/// assert_eq!("Salut, lume", romanian_hello_world.payload.get().message);
110///
111/// // We should not be able to load "en" data because it is not in either provider:
112///
113/// DataProvider::<HelloWorldV1>::load(
114/// &provider,
115/// DataRequest {
116/// id: DataIdentifierBorrowed::for_locale(&langid!("en").into()),
117/// ..Default::default()
118/// }
119/// )
120/// .expect_err("No English data");
121/// ```
122#[derive(Debug, PartialEq, Eq)]
123#[allow(clippy::exhaustive_structs)] // empty type
124pub struct IdentifierNotFoundPredicate;
125
126impl ForkByErrorPredicate for IdentifierNotFoundPredicate {
127 const UNIT_ERROR: DataErrorKind = DataErrorKind::IdentifierNotFound;
128
129 #[inline]
130 fn test(&self, _: DataMarkerInfo, _: Option<DataRequest>, err: DataError) -> bool {
131 Err::<(), _>(err).allow_identifier_not_found().is_ok()
132 }
133}