icu_provider/dynutil.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//! Utilities for using trait objects with `DataPayload`.
6
7/// Trait to allow conversion from `DataPayload<T>` to `DataPayload<S>`.
8///
9/// This trait can be manually implemented in order to enable [`impl_dynamic_data_provider`].
10pub trait UpcastDataPayload<M>
11where
12 M: crate::DynamicDataMarker,
13 Self: Sized + crate::DynamicDataMarker,
14{
15 /// Upcast a `DataPayload<T>` to a `DataPayload<S>` where `T` implements trait `S`.
16 fn upcast(other: crate::DataPayload<M>) -> crate::DataPayload<Self>;
17}
18
19/// Implements [`UpcastDataPayload`] from several data markers to a single data marker
20/// that all share the same [`DynamicDataMarker::DataStruct`].
21///
22/// # Examples
23///
24/// ```
25/// use icu_provider::prelude::*;
26/// use std::borrow::Cow;
27///
28/// struct FooV1;
29/// impl DynamicDataMarker for FooV1 {
30/// type DataStruct = Foo<'static>;
31/// }
32/// icu_provider::data_marker!(BarV1, Foo<'static>);
33/// icu_provider::data_marker!(BazV1, Foo<'static>);
34///
35/// #[derive(yoke::Yokeable)]
36/// pub struct Foo<'data> {
37/// message: Cow<'data, str>,
38/// };
39///
40/// icu_provider::data_struct!(Foo<'_>);
41///
42/// icu_provider::dynutil::impl_casting_upcast!(FooV1, [BarV1, BazV1,]);
43/// ```
44///
45/// [`DynamicDataMarker::DataStruct`]: crate::DynamicDataMarker::DataStruct
46#[macro_export]
47#[doc(hidden)] // macro
48macro_rules! __impl_casting_upcast {
49 ($dyn_m:path, [ $($struct_m:ident),+, ]) => {
50 $(
51 impl $crate::dynutil::UpcastDataPayload<$struct_m> for $dyn_m {
52 fn upcast(other: $crate::DataPayload<$struct_m>) -> $crate::DataPayload<$dyn_m> {
53 other.cast()
54 }
55 }
56 )+
57 }
58}
59#[doc(inline)]
60pub use __impl_casting_upcast as impl_casting_upcast;
61
62/// Implements [`DynamicDataProvider`] for a marker type `S` on a type that already implements
63/// [`DynamicDataProvider`] or [`DataProvider`] for one or more `M`, where `M` is a concrete type
64/// that is convertible to `S` via [`UpcastDataPayload`].
65///
66/// ## Wrapping DataProvider
67///
68/// If your type implements [`DataProvider`], pass a list of markers as the second argument.
69/// This results in a `DynamicDataProvider` that delegates to a specific marker if the marker
70/// matches or else returns [`DataErrorKind::MarkerNotFound`].
71///
72/// [`DynamicDataProvider`]: crate::DynamicDataProvider
73/// [`DataProvider`]: crate::DataProvider
74/// [`DataErrorKind::MarkerNotFound`]: (crate::DataErrorKind::MarkerNotFound)
75/// [`SerializeMarker`]: (crate::buf::SerializeMarker)
76#[doc(hidden)] // macro
77#[macro_export]
78macro_rules! __impl_dynamic_data_provider {
79 // allow passing in multiple things to do and get dispatched
80 ($provider:ty, $arms:tt, $one:path, $($rest:path),+) => {
81 $crate::dynutil::impl_dynamic_data_provider!(
82 $provider,
83 $arms,
84 $one
85 );
86
87 $crate::dynutil::impl_dynamic_data_provider!(
88 $provider,
89 $arms,
90 $($rest),+
91 );
92 };
93
94 ($provider:ty, { $($ident:ident = $marker:path => $struct_m:ty),+, $(_ => $struct_d:ty,)?}, $dyn_m:ty) => {
95 impl $crate::DynamicDataProvider<$dyn_m> for $provider
96 {
97 fn load_data(
98 &self,
99 marker: $crate::DataMarkerInfo,
100 req: $crate::DataRequest,
101 ) -> Result<
102 $crate::DataResponse<$dyn_m>,
103 $crate::DataError,
104 > {
105 match marker.id.hashed() {
106 $(
107 h if h == $marker.id.hashed() => {
108 let result: $crate::DataResponse<$struct_m> =
109 $crate::DynamicDataProvider::<$struct_m>::load_data(self, marker, req)?;
110 Ok($crate::DataResponse {
111 metadata: result.metadata,
112 payload: $crate::dynutil::UpcastDataPayload::<$struct_m>::upcast(result.payload),
113 })
114 }
115 )+,
116 $(
117 _ => {
118 let result: $crate::DataResponse<$struct_d> =
119 $crate::DynamicDataProvider::<$struct_d>::load_data(self, marker, req)?;
120 Ok($crate::DataResponse {
121 metadata: result.metadata,
122 payload: $crate::dynutil::UpcastDataPayload::<$struct_d>::upcast(result.payload),
123 })
124 }
125 )?
126 _ => Err($crate::DataErrorKind::MarkerNotFound.with_req(marker, req))
127 }
128 }
129 }
130
131 };
132 ($provider:ty, [ $($(#[$cfg:meta])? $struct_m:ty),+, ], $dyn_m:path) => {
133 impl $crate::DynamicDataProvider<$dyn_m> for $provider
134 {
135 fn load_data(
136 &self,
137 marker: $crate::DataMarkerInfo,
138 req: $crate::DataRequest,
139 ) -> Result<
140 $crate::DataResponse<$dyn_m>,
141 $crate::DataError,
142 > {
143 match marker.id.hashed() {
144 $(
145 $(#[$cfg])?
146 h if h == <$struct_m as $crate::DataMarker>::INFO.id.hashed() => {
147 let result: $crate::DataResponse<$struct_m> =
148 $crate::DataProvider::load(self, req)?;
149 Ok($crate::DataResponse {
150 metadata: result.metadata,
151 payload: $crate::dynutil::UpcastDataPayload::<$struct_m>::upcast(result.payload),
152 })
153 }
154 )+,
155 _ => Err($crate::DataErrorKind::MarkerNotFound.with_req(marker, req))
156 }
157 }
158 }
159 };
160}
161#[doc(inline)]
162pub use __impl_dynamic_data_provider as impl_dynamic_data_provider;
163
164#[doc(hidden)] // macro
165#[macro_export]
166macro_rules! __impl_iterable_dynamic_data_provider {
167 ($provider:ty, [ $($(#[$cfg:meta])? $struct_m:ty),+, ], $dyn_m:path) => {
168 impl $crate::IterableDynamicDataProvider<$dyn_m> for $provider {
169 fn iter_ids_for_marker(&self, marker: $crate::DataMarkerInfo) -> Result<alloc::collections::BTreeSet<$crate::DataIdentifierCow>, $crate::DataError> {
170 match marker.id.hashed() {
171 $(
172 $(#[$cfg])?
173 h if h == <$struct_m as $crate::DataMarker>::INFO.id.hashed() => {
174 $crate::IterableDataProvider::<$struct_m>::iter_ids(self)
175 }
176 )+,
177 _ => Err($crate::DataErrorKind::MarkerNotFound.with_marker(marker))
178 }
179 }
180 }
181 }
182}
183#[doc(inline)]
184pub use __impl_iterable_dynamic_data_provider as impl_iterable_dynamic_data_provider;