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
5use super::*;
6use alloc::boxed::Box;
7use icu_provider::prelude::*;
8
9use icu_locid::LanguageIdentifier;
10
11type RequestFilterDataProviderOutput<'a, D> =
12 RequestFilterDataProvider<D, Box<dyn Fn(DataRequest) -> bool + Sync + 'a>>;
13
14impl<D, F> RequestFilterDataProvider<D, F>
15where
16 F: Fn(DataRequest) -> bool + Sync,
17{
18 /// Filter out data requests with certain langids according to the predicate function. The
19 /// predicate should return `true` to allow a langid and `false` to reject a langid.
20 ///
21 /// Data requests with no langid will be allowed. To reject data requests without a langid,
22 /// chain this with [`Self::require_langid`].
23 ///
24 /// # Examples
25 ///
26 /// ```
27 /// use icu_locid::LanguageIdentifier;
28 /// use icu_locid::{langid, locale, subtags::language};
29 /// use icu_provider::datagen::*;
30 /// use icu_provider::hello_world::*;
31 /// use icu_provider::prelude::*;
32 /// use icu_provider_adapters::filter::Filterable;
33 ///
34 /// let provider = HelloWorldProvider
35 /// .filterable("Demo no-English filter")
36 /// .filter_by_langid(|langid| langid.language != language!("en"));
37 ///
38 /// // German requests should succeed:
39 /// let req_de = DataRequest {
40 /// locale: &locale!("de").into(),
41 /// metadata: Default::default(),
42 /// };
43 /// let response: Result<DataResponse<HelloWorldV1Marker>, _> =
44 /// provider.load(req_de);
45 /// assert!(matches!(response, Ok(_)));
46 ///
47 /// // English requests should fail:
48 /// let req_en = DataRequest {
49 /// locale: &locale!("en-US").into(),
50 /// metadata: Default::default(),
51 /// };
52 /// let response: Result<DataResponse<HelloWorldV1Marker>, _> =
53 /// provider.load(req_en);
54 /// assert!(matches!(
55 /// response,
56 /// Err(DataError {
57 /// kind: DataErrorKind::FilteredResource,
58 /// ..
59 /// })
60 /// ));
61 ///
62 /// // English should not appear in the iterator result:
63 /// let supported_langids = provider
64 /// .supported_locales()
65 /// .expect("Should successfully make an iterator of supported locales")
66 /// .into_iter()
67 /// .map(|options| options.get_langid())
68 /// .collect::<Vec<LanguageIdentifier>>();
69 /// assert!(supported_langids.contains(&langid!("de")));
70 /// assert!(!supported_langids.contains(&langid!("en")));
71 /// ```
72 pub fn filter_by_langid<'a>(
73 self,
74 predicate: impl Fn(&LanguageIdentifier) -> bool + Sync + 'a,
75 ) -> RequestFilterDataProviderOutput<'a, D>
76 where
77 F: 'a,
78 {
79 let old_predicate = self.predicate;
80 RequestFilterDataProvider {
81 inner: self.inner,
82 predicate: Box::new(move |request| -> bool {
83 if !(old_predicate)(request) {
84 return false;
85 }
86 predicate(&request.locale.get_langid())
87 }),
88 filter_name: self.filter_name,
89 }
90 }
91
92 /// Filter out data request except those having a language identifier that exactly matches
93 /// one in the allowlist.
94 ///
95 /// This will be replaced with a smarter algorithm for locale filtering; see
96 /// <https://github.com/unicode-org/icu4x/issues/834>
97 ///
98 /// Data requests with no langid will be allowed. To reject data requests without a langid,
99 /// chain this with [`Self::require_langid`].
100 ///
101 /// # Examples
102 ///
103 /// ```
104 /// use icu_locid::{langid, locale};
105 /// use icu_provider::hello_world::*;
106 /// use icu_provider::prelude::*;
107 /// use icu_provider_adapters::filter::Filterable;
108 ///
109 /// let allowlist = [langid!("de"), langid!("zh")];
110 /// let provider = HelloWorldProvider
111 /// .filterable("Demo German+Chinese filter")
112 /// .filter_by_langid_allowlist_strict(&allowlist);
113 ///
114 /// // German requests should succeed:
115 /// let req_de = DataRequest {
116 /// locale: &locale!("de").into(),
117 /// metadata: Default::default(),
118 /// };
119 /// let response: Result<DataResponse<HelloWorldV1Marker>, _> =
120 /// provider.load(req_de);
121 /// assert!(matches!(response, Ok(_)));
122 ///
123 /// // English requests should fail:
124 /// let req_en = DataRequest {
125 /// locale: &locale!("en-US").into(),
126 /// metadata: Default::default(),
127 /// };
128 /// let response: Result<DataResponse<HelloWorldV1Marker>, _> =
129 /// provider.load(req_en);
130 /// assert!(matches!(
131 /// response,
132 /// Err(DataError {
133 /// kind: DataErrorKind::FilteredResource,
134 /// ..
135 /// })
136 /// ));
137 /// assert_eq!(
138 /// response.unwrap_err().str_context,
139 /// Some("Demo German+Chinese filter")
140 /// );
141 /// ```
142 pub fn filter_by_langid_allowlist_strict<'a>(
143 self,
144 allowlist: &'a [LanguageIdentifier],
145 ) -> RequestFilterDataProviderOutput<'a, D>
146 where
147 F: 'a,
148 {
149 let old_predicate = self.predicate;
150 RequestFilterDataProvider {
151 inner: self.inner,
152 predicate: Box::new(move |request| -> bool {
153 if !(old_predicate)(request) {
154 return false;
155 }
156 request.locale.is_langid_und() || allowlist.contains(&request.locale.get_langid())
157 }),
158 filter_name: self.filter_name,
159 }
160 }
161
162 /// Require that data requests contain a langid.
163 ///
164 /// # Examples
165 ///
166 /// ```
167 /// use icu_locid::locale;
168 /// use icu_provider::hello_world::*;
169 /// use icu_provider::prelude::*;
170 /// use icu_provider_adapters::filter::Filterable;
171 ///
172 /// let provider = HelloWorldProvider
173 /// .filterable("Demo require-langid filter")
174 /// .require_langid();
175 ///
176 /// // Requests with a langid should succeed:
177 /// let req_with_langid = DataRequest {
178 /// locale: &locale!("de").into(),
179 /// metadata: Default::default(),
180 /// };
181 /// let response: Result<DataResponse<HelloWorldV1Marker>, _> =
182 /// provider.load(req_with_langid);
183 /// assert!(matches!(response, Ok(_)));
184 ///
185 /// // Requests without a langid should fail:
186 /// let req_no_langid = DataRequest {
187 /// locale: Default::default(),
188 /// metadata: Default::default(),
189 /// };
190 /// let response: Result<DataResponse<HelloWorldV1Marker>, _> =
191 /// provider.load(req_no_langid);
192 /// assert!(matches!(
193 /// response,
194 /// Err(DataError {
195 /// kind: DataErrorKind::FilteredResource,
196 /// ..
197 /// })
198 /// ));
199 /// ```
200 pub fn require_langid<'a>(self) -> RequestFilterDataProviderOutput<'a, D>
201 where
202 F: 'a,
203 {
204 let old_predicate = self.predicate;
205 RequestFilterDataProvider {
206 inner: self.inner,
207 predicate: Box::new(move |request| -> bool {
208 if !(old_predicate)(request) {
209 return false;
210 }
211 !request.locale.is_langid_und()
212 }),
213 filter_name: self.filter_name,
214 }
215 }
216}
217