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 | use core::marker::PhantomData; |
6 | use yoke::Yokeable; |
7 | |
8 | use crate::error::DataError; |
9 | use crate::key::DataKey; |
10 | use crate::marker::{DataMarker, KeyedDataMarker}; |
11 | use crate::request::DataRequest; |
12 | use crate::response::DataResponse; |
13 | |
14 | /// A data provider that loads data for a specific [`DataKey`]. |
15 | pub trait DataProvider<M> |
16 | where |
17 | M: KeyedDataMarker, |
18 | { |
19 | /// Query the provider for data, returning the result. |
20 | /// |
21 | /// Returns [`Ok`] if the request successfully loaded data. If data failed to load, returns an |
22 | /// Error with more information. |
23 | fn load(&self, req: DataRequest) -> Result<DataResponse<M>, DataError>; |
24 | } |
25 | |
26 | impl<'a, M, P> DataProvider<M> for &'a P |
27 | where |
28 | M: KeyedDataMarker, |
29 | P: DataProvider<M> + ?Sized, |
30 | { |
31 | #[inline] |
32 | fn load(&self, req: DataRequest) -> Result<DataResponse<M>, DataError> { |
33 | (*self).load(req) |
34 | } |
35 | } |
36 | |
37 | impl<M, P> DataProvider<M> for alloc::boxed::Box<P> |
38 | where |
39 | M: KeyedDataMarker, |
40 | P: DataProvider<M> + ?Sized, |
41 | { |
42 | #[inline] |
43 | fn load(&self, req: DataRequest) -> Result<DataResponse<M>, DataError> { |
44 | (**self).load(req) |
45 | } |
46 | } |
47 | |
48 | impl<M, P> DataProvider<M> for alloc::rc::Rc<P> |
49 | where |
50 | M: KeyedDataMarker, |
51 | P: DataProvider<M> + ?Sized, |
52 | { |
53 | #[inline] |
54 | fn load(&self, req: DataRequest) -> Result<DataResponse<M>, DataError> { |
55 | (**self).load(req) |
56 | } |
57 | } |
58 | |
59 | #[cfg(target_has_atomic = "ptr")] |
60 | impl<M, P> DataProvider<M> for alloc::sync::Arc<P> |
61 | where |
62 | M: KeyedDataMarker, |
63 | P: DataProvider<M> + ?Sized, |
64 | { |
65 | #[inline] |
66 | fn load(&self, req: DataRequest) -> Result<DataResponse<M>, DataError> { |
67 | (**self).load(req) |
68 | } |
69 | } |
70 | |
71 | /// A data provider that loads data for a specific data type. |
72 | /// |
73 | /// Unlike [`DataProvider`], there may be multiple keys corresponding to the same data type. |
74 | /// This is often the case when returning `dyn` trait objects such as [`AnyMarker`]. |
75 | /// |
76 | /// [`AnyMarker`]: crate::any::AnyMarker |
77 | pub trait DynamicDataProvider<M> |
78 | where |
79 | M: DataMarker, |
80 | { |
81 | /// Query the provider for data, returning the result. |
82 | /// |
83 | /// Returns [`Ok`] if the request successfully loaded data. If data failed to load, returns an |
84 | /// Error with more information. |
85 | fn load_data(&self, key: DataKey, req: DataRequest) -> Result<DataResponse<M>, DataError>; |
86 | } |
87 | |
88 | impl<'a, M, P> DynamicDataProvider<M> for &'a P |
89 | where |
90 | M: DataMarker, |
91 | P: DynamicDataProvider<M> + ?Sized, |
92 | { |
93 | #[inline] |
94 | fn load_data(&self, key: DataKey, req: DataRequest) -> Result<DataResponse<M>, DataError> { |
95 | (*self).load_data(key, req) |
96 | } |
97 | } |
98 | |
99 | impl<M, P> DynamicDataProvider<M> for alloc::boxed::Box<P> |
100 | where |
101 | M: DataMarker, |
102 | P: DynamicDataProvider<M> + ?Sized, |
103 | { |
104 | #[inline] |
105 | fn load_data(&self, key: DataKey, req: DataRequest) -> Result<DataResponse<M>, DataError> { |
106 | (**self).load_data(key, req) |
107 | } |
108 | } |
109 | |
110 | impl<M, P> DynamicDataProvider<M> for alloc::rc::Rc<P> |
111 | where |
112 | M: DataMarker, |
113 | P: DynamicDataProvider<M> + ?Sized, |
114 | { |
115 | #[inline] |
116 | fn load_data(&self, key: DataKey, req: DataRequest) -> Result<DataResponse<M>, DataError> { |
117 | (**self).load_data(key, req) |
118 | } |
119 | } |
120 | |
121 | #[cfg(target_has_atomic = "ptr")] |
122 | impl<M, P> DynamicDataProvider<M> for alloc::sync::Arc<P> |
123 | where |
124 | M: DataMarker, |
125 | P: DynamicDataProvider<M> + ?Sized, |
126 | { |
127 | #[inline] |
128 | fn load_data(&self, key: DataKey, req: DataRequest) -> Result<DataResponse<M>, DataError> { |
129 | (**self).load_data(key, req) |
130 | } |
131 | } |
132 | |
133 | /// A data provider that loads data for a specific data type. |
134 | /// |
135 | /// Unlike [`DataProvider`], the provider is bound to a specific key ahead of time. |
136 | /// |
137 | /// This crate provides [`DataProviderWithKey`] which implements this trait on a single provider |
138 | /// with a single key. However, this trait can also be implemented on providers that fork between |
139 | /// multiple keys that all return the same data type. For example, it can abstract over many |
140 | /// calendar systems in the datetime formatter. |
141 | /// |
142 | /// [`AnyMarker`]: crate::any::AnyMarker |
143 | pub trait BoundDataProvider<M> |
144 | where |
145 | M: DataMarker, |
146 | { |
147 | /// Query the provider for data, returning the result. |
148 | /// |
149 | /// Returns [`Ok`] if the request successfully loaded data. If data failed to load, returns an |
150 | /// Error with more information. |
151 | fn load_bound(&self, req: DataRequest) -> Result<DataResponse<M>, DataError>; |
152 | /// Returns the [`DataKey`] that this provider uses for loading data. |
153 | fn bound_key(&self) -> DataKey; |
154 | } |
155 | |
156 | impl<'a, M, P> BoundDataProvider<M> for &'a P |
157 | where |
158 | M: DataMarker, |
159 | P: BoundDataProvider<M> + ?Sized, |
160 | { |
161 | #[inline] |
162 | fn load_bound(&self, req: DataRequest) -> Result<DataResponse<M>, DataError> { |
163 | (*self).load_bound(req) |
164 | } |
165 | #[inline] |
166 | fn bound_key(&self) -> DataKey { |
167 | (*self).bound_key() |
168 | } |
169 | } |
170 | |
171 | impl<M, P> BoundDataProvider<M> for alloc::boxed::Box<P> |
172 | where |
173 | M: DataMarker, |
174 | P: BoundDataProvider<M> + ?Sized, |
175 | { |
176 | #[inline] |
177 | fn load_bound(&self, req: DataRequest) -> Result<DataResponse<M>, DataError> { |
178 | (**self).load_bound(req) |
179 | } |
180 | #[inline] |
181 | fn bound_key(&self) -> DataKey { |
182 | (**self).bound_key() |
183 | } |
184 | } |
185 | |
186 | impl<M, P> BoundDataProvider<M> for alloc::rc::Rc<P> |
187 | where |
188 | M: DataMarker, |
189 | P: BoundDataProvider<M> + ?Sized, |
190 | { |
191 | #[inline] |
192 | fn load_bound(&self, req: DataRequest) -> Result<DataResponse<M>, DataError> { |
193 | (**self).load_bound(req) |
194 | } |
195 | #[inline] |
196 | fn bound_key(&self) -> DataKey { |
197 | (**self).bound_key() |
198 | } |
199 | } |
200 | |
201 | #[cfg(target_has_atomic = "ptr")] |
202 | impl<M, P> BoundDataProvider<M> for alloc::sync::Arc<P> |
203 | where |
204 | M: DataMarker, |
205 | P: BoundDataProvider<M> + ?Sized, |
206 | { |
207 | #[inline] |
208 | fn load_bound(&self, req: DataRequest) -> Result<DataResponse<M>, DataError> { |
209 | (**self).load_bound(req) |
210 | } |
211 | #[inline] |
212 | fn bound_key(&self) -> DataKey { |
213 | (**self).bound_key() |
214 | } |
215 | } |
216 | |
217 | /// A [`DataProvider`] associated with a specific key. |
218 | /// |
219 | /// Implements [`BoundDataProvider`]. |
220 | #[derive(Debug)] |
221 | pub struct DataProviderWithKey<M, P> { |
222 | inner: P, |
223 | _marker: PhantomData<M>, |
224 | } |
225 | |
226 | impl<M, P> DataProviderWithKey<M, P> |
227 | where |
228 | M: KeyedDataMarker, |
229 | P: DataProvider<M>, |
230 | { |
231 | /// Creates a [`DataProviderWithKey`] from a [`DataProvider`] with a [`KeyedDataMarker`]. |
232 | pub const fn new(inner: P) -> Self { |
233 | Self { |
234 | inner, |
235 | _marker: PhantomData, |
236 | } |
237 | } |
238 | } |
239 | |
240 | impl<M, M0, Y, P> BoundDataProvider<M0> for DataProviderWithKey<M, P> |
241 | where |
242 | M: KeyedDataMarker<Yokeable = Y>, |
243 | M0: DataMarker<Yokeable = Y>, |
244 | Y: for<'a> dynYokeable<'a>, |
245 | P: DataProvider<M>, |
246 | { |
247 | #[inline] |
248 | fn load_bound(&self, req: DataRequest) -> Result<DataResponse<M0>, DataError> { |
249 | self.inner.load(req).map(op:DataResponse::cast) |
250 | } |
251 | #[inline] |
252 | fn bound_key(&self) -> DataKey { |
253 | M::KEY |
254 | } |
255 | } |
256 | |
257 | #[cfg(test)] |
258 | mod test { |
259 | |
260 | use super::*; |
261 | use crate::hello_world::*; |
262 | use crate::prelude::*; |
263 | use alloc::borrow::Cow; |
264 | use alloc::string::String; |
265 | use core::fmt::Debug; |
266 | use serde::{Deserialize, Serialize}; |
267 | |
268 | // This tests DataProvider borrow semantics with a dummy data provider based on a |
269 | // JSON string. It also exercises most of the data provider code paths. |
270 | |
271 | /// Key for HelloAlt, used for testing mismatched types |
272 | const HELLO_ALT_KEY: DataKey = crate::data_key!("core/helloalt@1"); |
273 | |
274 | /// A data struct serialization-compatible with HelloWorldV1 used for testing mismatched types |
275 | #[derive( |
276 | Serialize, Deserialize, Debug, Clone, Default, PartialEq, yoke::Yokeable, zerofrom::ZeroFrom, |
277 | )] |
278 | struct HelloAlt { |
279 | #[zerofrom(clone)] |
280 | message: String, |
281 | } |
282 | |
283 | /// Marker type for [`HelloAlt`]. |
284 | struct HelloAltMarker {} |
285 | |
286 | impl DataMarker for HelloAltMarker { |
287 | type Yokeable = HelloAlt; |
288 | } |
289 | |
290 | impl KeyedDataMarker for HelloAltMarker { |
291 | const KEY: DataKey = HELLO_ALT_KEY; |
292 | } |
293 | |
294 | #[derive(Deserialize, Debug, Clone, Default, PartialEq)] |
295 | struct HelloCombined<'data> { |
296 | #[serde(borrow)] |
297 | pub hello_v1: HelloWorldV1<'data>, |
298 | pub hello_alt: HelloAlt, |
299 | } |
300 | |
301 | /// A DataProvider that owns its data, returning an Rc-variant DataPayload. |
302 | /// Supports only key::HELLO_WORLD_V1. Uses `impl_dynamic_data_provider!()`. |
303 | #[derive(Debug)] |
304 | struct DataWarehouse { |
305 | hello_v1: HelloWorldV1<'static>, |
306 | hello_alt: HelloAlt, |
307 | } |
308 | |
309 | impl DataProvider<HelloWorldV1Marker> for DataWarehouse { |
310 | fn load(&self, _: DataRequest) -> Result<DataResponse<HelloWorldV1Marker>, DataError> { |
311 | Ok(DataResponse { |
312 | metadata: DataResponseMetadata::default(), |
313 | payload: Some(DataPayload::from_owned(self.hello_v1.clone())), |
314 | }) |
315 | } |
316 | } |
317 | |
318 | crate::impl_dynamic_data_provider!(DataWarehouse, [HelloWorldV1Marker,], AnyMarker); |
319 | |
320 | /// A DataProvider that supports both key::HELLO_WORLD_V1 and HELLO_ALT. |
321 | #[derive(Debug)] |
322 | struct DataProvider2 { |
323 | data: DataWarehouse, |
324 | } |
325 | |
326 | impl From<DataWarehouse> for DataProvider2 { |
327 | fn from(warehouse: DataWarehouse) -> Self { |
328 | DataProvider2 { data: warehouse } |
329 | } |
330 | } |
331 | |
332 | impl DataProvider<HelloWorldV1Marker> for DataProvider2 { |
333 | fn load(&self, _: DataRequest) -> Result<DataResponse<HelloWorldV1Marker>, DataError> { |
334 | Ok(DataResponse { |
335 | metadata: DataResponseMetadata::default(), |
336 | payload: Some(DataPayload::from_owned(self.data.hello_v1.clone())), |
337 | }) |
338 | } |
339 | } |
340 | |
341 | impl DataProvider<HelloAltMarker> for DataProvider2 { |
342 | fn load(&self, _: DataRequest) -> Result<DataResponse<HelloAltMarker>, DataError> { |
343 | Ok(DataResponse { |
344 | metadata: DataResponseMetadata::default(), |
345 | payload: Some(DataPayload::from_owned(self.data.hello_alt.clone())), |
346 | }) |
347 | } |
348 | } |
349 | |
350 | crate::impl_dynamic_data_provider!( |
351 | DataProvider2, |
352 | [HelloWorldV1Marker, HelloAltMarker,], |
353 | AnyMarker |
354 | ); |
355 | |
356 | const DATA: &str = r#"{ |
357 | "hello_v1": { |
358 | "message": "Hello V1" |
359 | }, |
360 | "hello_alt": { |
361 | "message": "Hello Alt" |
362 | } |
363 | }"#; |
364 | |
365 | fn get_warehouse(data: &'static str) -> DataWarehouse { |
366 | let data: HelloCombined = serde_json::from_str(data).expect("Well-formed data"); |
367 | DataWarehouse { |
368 | hello_v1: data.hello_v1, |
369 | hello_alt: data.hello_alt, |
370 | } |
371 | } |
372 | |
373 | fn get_payload_v1<P: DataProvider<HelloWorldV1Marker> + ?Sized>( |
374 | provider: &P, |
375 | ) -> Result<DataPayload<HelloWorldV1Marker>, DataError> { |
376 | provider.load(Default::default())?.take_payload() |
377 | } |
378 | |
379 | fn get_payload_alt<P: DataProvider<HelloAltMarker> + ?Sized>( |
380 | provider: &P, |
381 | ) -> Result<DataPayload<HelloAltMarker>, DataError> { |
382 | provider.load(Default::default())?.take_payload() |
383 | } |
384 | |
385 | #[test] |
386 | fn test_warehouse_owned() { |
387 | let warehouse = get_warehouse(DATA); |
388 | let hello_data = get_payload_v1(&warehouse).unwrap(); |
389 | assert!(matches!( |
390 | hello_data.get(), |
391 | HelloWorldV1 { |
392 | message: Cow::Borrowed(_), |
393 | } |
394 | )); |
395 | } |
396 | |
397 | #[test] |
398 | fn test_warehouse_owned_dyn_erased() { |
399 | let warehouse = get_warehouse(DATA); |
400 | let hello_data = get_payload_v1(&warehouse.as_any_provider().as_downcasting()).unwrap(); |
401 | assert!(matches!( |
402 | hello_data.get(), |
403 | HelloWorldV1 { |
404 | message: Cow::Borrowed(_), |
405 | } |
406 | )); |
407 | } |
408 | |
409 | #[test] |
410 | fn test_warehouse_owned_dyn_generic() { |
411 | let warehouse = get_warehouse(DATA); |
412 | let hello_data = |
413 | get_payload_v1(&warehouse as &dyn DataProvider<HelloWorldV1Marker>).unwrap(); |
414 | assert!(matches!( |
415 | hello_data.get(), |
416 | HelloWorldV1 { |
417 | message: Cow::Borrowed(_), |
418 | } |
419 | )); |
420 | } |
421 | |
422 | #[test] |
423 | fn test_warehouse_owned_dyn_erased_alt() { |
424 | let warehouse = get_warehouse(DATA); |
425 | let response = get_payload_alt(&warehouse.as_any_provider().as_downcasting()); |
426 | assert!(matches!( |
427 | response, |
428 | Err(DataError { |
429 | kind: DataErrorKind::MissingDataKey, |
430 | .. |
431 | }) |
432 | )); |
433 | } |
434 | |
435 | #[test] |
436 | fn test_provider2() { |
437 | let warehouse = get_warehouse(DATA); |
438 | let provider = DataProvider2::from(warehouse); |
439 | let hello_data = get_payload_v1(&provider).unwrap(); |
440 | assert!(matches!( |
441 | hello_data.get(), |
442 | HelloWorldV1 { |
443 | message: Cow::Borrowed(_), |
444 | } |
445 | )); |
446 | } |
447 | |
448 | #[test] |
449 | fn test_provider2_dyn_erased() { |
450 | let warehouse = get_warehouse(DATA); |
451 | let provider = DataProvider2::from(warehouse); |
452 | let hello_data = get_payload_v1(&provider.as_any_provider().as_downcasting()).unwrap(); |
453 | assert!(matches!( |
454 | hello_data.get(), |
455 | HelloWorldV1 { |
456 | message: Cow::Borrowed(_), |
457 | } |
458 | )); |
459 | } |
460 | |
461 | #[test] |
462 | fn test_provider2_dyn_erased_alt() { |
463 | let warehouse = get_warehouse(DATA); |
464 | let provider = DataProvider2::from(warehouse); |
465 | let hello_data = get_payload_alt(&provider.as_any_provider().as_downcasting()).unwrap(); |
466 | assert!(matches!(hello_data.get(), HelloAlt { .. })); |
467 | } |
468 | |
469 | #[test] |
470 | fn test_provider2_dyn_generic() { |
471 | let warehouse = get_warehouse(DATA); |
472 | let provider = DataProvider2::from(warehouse); |
473 | let hello_data = |
474 | get_payload_v1(&provider as &dyn DataProvider<HelloWorldV1Marker>).unwrap(); |
475 | assert!(matches!( |
476 | hello_data.get(), |
477 | HelloWorldV1 { |
478 | message: Cow::Borrowed(_), |
479 | } |
480 | )); |
481 | } |
482 | |
483 | #[test] |
484 | fn test_provider2_dyn_generic_alt() { |
485 | let warehouse = get_warehouse(DATA); |
486 | let provider = DataProvider2::from(warehouse); |
487 | let hello_data = get_payload_alt(&provider as &dyn DataProvider<HelloAltMarker>).unwrap(); |
488 | assert!(matches!(hello_data.get(), HelloAlt { .. })); |
489 | } |
490 | |
491 | #[test] |
492 | fn test_mismatched_types() { |
493 | let warehouse = get_warehouse(DATA); |
494 | let provider = DataProvider2::from(warehouse); |
495 | // Request is for v2, but type argument is for v1 |
496 | let response: Result<DataResponse<HelloWorldV1Marker>, DataError> = AnyProvider::load_any( |
497 | &provider.as_any_provider(), |
498 | HELLO_ALT_KEY, |
499 | Default::default(), |
500 | ) |
501 | .unwrap() |
502 | .downcast(); |
503 | assert!(matches!( |
504 | response, |
505 | Err(DataError { |
506 | kind: DataErrorKind::MismatchedType(_), |
507 | .. |
508 | }) |
509 | )); |
510 | } |
511 | |
512 | fn check_v1_v2<P>(d: &P) |
513 | where |
514 | P: DataProvider<HelloWorldV1Marker> + DataProvider<HelloAltMarker> + ?Sized, |
515 | { |
516 | let v1: DataPayload<HelloWorldV1Marker> = |
517 | d.load(Default::default()).unwrap().take_payload().unwrap(); |
518 | let v2: DataPayload<HelloAltMarker> = |
519 | d.load(Default::default()).unwrap().take_payload().unwrap(); |
520 | if v1.get().message == v2.get().message { |
521 | panic!() |
522 | } |
523 | } |
524 | |
525 | #[test] |
526 | fn test_v1_v2_generic() { |
527 | let warehouse = get_warehouse(DATA); |
528 | let provider = DataProvider2::from(warehouse); |
529 | check_v1_v2(&provider); |
530 | } |
531 | |
532 | #[test] |
533 | fn test_v1_v2_dyn_erased() { |
534 | let warehouse = get_warehouse(DATA); |
535 | let provider = DataProvider2::from(warehouse); |
536 | check_v1_v2(&provider.as_any_provider().as_downcasting()); |
537 | } |
538 | } |
539 |
Definitions
Learn Rust with the experts
Find out more