icu_provider/
data_provider.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
5use core::marker::PhantomData;
6use yoke::Yokeable;
7
8use crate::error::DataError;
9use crate::key::DataKey;
10use crate::marker::{DataMarker, KeyedDataMarker};
11use crate::request::DataRequest;
12use crate::response::DataResponse;
13
14/// A data provider that loads data for a specific [`DataKey`].
15pub trait DataProvider<M>
16where
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
26impl<'a, M, P> DataProvider<M> for &'a P
27where
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
37impl<M, P> DataProvider<M> for alloc::boxed::Box<P>
38where
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
48impl<M, P> DataProvider<M> for alloc::rc::Rc<P>
49where
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")]
60impl<M, P> DataProvider<M> for alloc::sync::Arc<P>
61where
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
77pub trait DynamicDataProvider<M>
78where
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
88impl<'a, M, P> DynamicDataProvider<M> for &'a P
89where
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
99impl<M, P> DynamicDataProvider<M> for alloc::boxed::Box<P>
100where
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
110impl<M, P> DynamicDataProvider<M> for alloc::rc::Rc<P>
111where
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")]
122impl<M, P> DynamicDataProvider<M> for alloc::sync::Arc<P>
123where
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
143pub trait BoundDataProvider<M>
144where
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
156impl<'a, M, P> BoundDataProvider<M> for &'a P
157where
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
171impl<M, P> BoundDataProvider<M> for alloc::boxed::Box<P>
172where
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
186impl<M, P> BoundDataProvider<M> for alloc::rc::Rc<P>
187where
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")]
202impl<M, P> BoundDataProvider<M> for alloc::sync::Arc<P>
203where
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)]
221pub struct DataProviderWithKey<M, P> {
222    inner: P,
223    _marker: PhantomData<M>,
224}
225
226impl<M, P> DataProviderWithKey<M, P>
227where
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
240impl<M, M0, Y, P> BoundDataProvider<M0> for DataProviderWithKey<M, P>
241where
242    M: KeyedDataMarker<Yokeable = Y>,
243    M0: DataMarker<Yokeable = Y>,
244    Y: for<'a> Yokeable<'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(DataResponse::cast)
250    }
251    #[inline]
252    fn bound_key(&self) -> DataKey {
253        M::KEY
254    }
255}
256
257#[cfg(test)]
258mod 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}