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::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::langid;
77///
78/// // The `tests` directory contains two separate "language packs" for Hello World data.
79/// let provider_de = FsDataProvider::try_new("tests/data/langtest/de").unwrap();
80/// let provider_ro = FsDataProvider::try_new("tests/data/langtest/ro").unwrap();
81///
82/// // Create the forking provider:
83/// let provider = ForkByErrorProvider::new_with_predicate(
84///     provider_de,
85///     provider_ro,
86///     MissingLocalePredicate
87/// );
88///
89/// // Test that we can load both "de" and "ro" data:
90///
91/// let german_hello_world: DataPayload<HelloWorldV1Marker> = provider
92///     .as_deserializing()
93///     .load(DataRequest {
94///         locale: &langid!("de").into(),
95///         metadata: Default::default(),
96///     })
97///     .expect("Loading should succeed")
98///     .take_payload()
99///     .expect("Data should be present");
100///
101/// assert_eq!("Hallo Welt", german_hello_world.get().message);
102///
103/// let romanian_hello_world: DataPayload<HelloWorldV1Marker> = provider
104///     .as_deserializing()
105///     .load(DataRequest {
106///         locale: &langid!("ro").into(),
107///         metadata: Default::default(),
108///     })
109///     .expect("Loading should succeed")
110///     .take_payload()
111///     .expect("Data should be present");
112///
113/// assert_eq!("Salut, lume", romanian_hello_world.get().message);
114///
115/// // We should not be able to load "en" data because it is not in the provider:
116///
117/// DataProvider::<HelloWorldV1Marker>::load(
118///     &provider.as_deserializing(),
119///     DataRequest {
120///         locale: &langid!("en").into(),
121///         metadata: Default::default(),
122///     }
123/// )
124/// .expect_err("No English data");
125/// ```
126#[derive(Debug, PartialEq, Eq)]
127#[allow(clippy::exhaustive_structs)] // empty type
128pub struct MissingLocalePredicate;
129
130impl ForkByErrorPredicate for MissingLocalePredicate {
131    const UNIT_ERROR: DataErrorKind = DataErrorKind::MissingLocale;
132
133    #[inline]
134    fn test(&self, _: DataKey, _: Option<DataRequest>, err: DataError) -> bool {
135        matches!(
136            err,
137            DataError {
138                kind: DataErrorKind::MissingLocale,
139                ..
140            }
141        )
142    }
143}