/rust/registry/src/index.crates.io-1949cf8c6b5b557f/icu_provider-1.5.0/src/dynutil.rs
Line | Count | Source |
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`](crate::impl_dynamic_data_provider). |
10 | | /// |
11 | | /// [`DataPayload::downcast`]: crate::DataPayload::downcast |
12 | | pub trait UpcastDataPayload<M> |
13 | | where |
14 | | M: crate::DataMarker, |
15 | | Self: Sized + crate::DataMarker, |
16 | | { |
17 | | /// Upcast a `DataPayload<T>` to a `DataPayload<S>` where `T` implements trait `S`. |
18 | | /// |
19 | | /// # Examples |
20 | | /// |
21 | | /// Upcast and then downcast a data struct of type `Cow<str>` (cart type `String`) via |
22 | | /// [`AnyPayload`](crate::any::AnyPayload): |
23 | | /// |
24 | | /// ``` |
25 | | /// use icu_provider::dynutil::UpcastDataPayload; |
26 | | /// use icu_provider::hello_world::*; |
27 | | /// use icu_provider::prelude::*; |
28 | | /// use std::borrow::Cow; |
29 | | /// |
30 | | /// let original = DataPayload::<HelloWorldV1Marker>::from_static_str("foo"); |
31 | | /// let upcasted = AnyMarker::upcast(original); |
32 | | /// let downcasted = upcasted |
33 | | /// .downcast::<HelloWorldV1Marker>() |
34 | | /// .expect("Type conversion"); |
35 | | /// assert_eq!(downcasted.get().message, "foo"); |
36 | | /// ``` |
37 | | fn upcast(other: crate::DataPayload<M>) -> crate::DataPayload<Self>; |
38 | | } |
39 | | |
40 | | /// Implements [`UpcastDataPayload`] from several data markers to a single data marker |
41 | | /// that all share the same [`DataMarker::Yokeable`]. |
42 | | /// |
43 | | /// # Examples |
44 | | /// |
45 | | /// ``` |
46 | | /// use icu_provider::prelude::*; |
47 | | /// use std::borrow::Cow; |
48 | | /// |
49 | | /// #[icu_provider::data_struct( |
50 | | /// FooV1Marker, |
51 | | /// BarV1Marker = "demo/bar@1", |
52 | | /// BazV1Marker = "demo/baz@1" |
53 | | /// )] |
54 | | /// pub struct FooV1<'data> { |
55 | | /// message: Cow<'data, str>, |
56 | | /// }; |
57 | | /// |
58 | | /// icu_provider::impl_casting_upcast!( |
59 | | /// FooV1Marker, |
60 | | /// [BarV1Marker, BazV1Marker,] |
61 | | /// ); |
62 | | /// ``` |
63 | | /// |
64 | | /// [`DataMarker::Yokeable`]: crate::DataMarker::Yokeable |
65 | | #[macro_export] |
66 | | macro_rules! impl_casting_upcast { |
67 | | ($dyn_m:path, [ $($struct_m:ident),+, ]) => { |
68 | | $( |
69 | | impl $crate::dynutil::UpcastDataPayload<$struct_m> for $dyn_m { |
70 | | fn upcast(other: $crate::DataPayload<$struct_m>) -> $crate::DataPayload<$dyn_m> { |
71 | | other.cast() |
72 | | } |
73 | | } |
74 | | )+ |
75 | | } |
76 | | } |
77 | | |
78 | | /// Implements [`DynamicDataProvider`] for a marker type `S` on a type that already implements |
79 | | /// [`DynamicDataProvider`] or [`DataProvider`] for one or more `M`, where `M` is a concrete type |
80 | | /// that is convertible to `S` via [`UpcastDataPayload`]. |
81 | | /// |
82 | | /// Use this macro to add support to your data provider for: |
83 | | /// |
84 | | /// - [`AnyPayload`] if your provider can return typed objects as [`Any`](core::any::Any). |
85 | | /// |
86 | | /// ## Wrapping DataProvider |
87 | | /// |
88 | | /// If your type implements [`DataProvider`], pass a list of markers as the second argument. |
89 | | /// This results in a `DynamicDataProvider` that delegates to a specific marker if the key |
90 | | /// matches or else returns [`DataErrorKind::MissingDataKey`]. |
91 | | /// |
92 | | /// ``` |
93 | | /// use icu_provider::prelude::*; |
94 | | /// use icu_provider::hello_world::*; |
95 | | /// # |
96 | | /// # // Duplicating HelloWorldProvider because the real one already implements DynamicDataProvider<AnyMarker> |
97 | | /// # struct HelloWorldProvider; |
98 | | /// # impl DataProvider<HelloWorldV1Marker> for HelloWorldProvider { |
99 | | /// # fn load( |
100 | | /// # &self, |
101 | | /// # req: DataRequest, |
102 | | /// # ) -> Result<DataResponse<HelloWorldV1Marker>, DataError> { |
103 | | /// # icu_provider::hello_world::HelloWorldProvider.load(req) |
104 | | /// # } |
105 | | /// # } |
106 | | /// |
107 | | /// // Implement DynamicDataProvider<AnyMarker> on HelloWorldProvider: DataProvider<HelloWorldV1Marker> |
108 | | /// icu_provider::impl_dynamic_data_provider!(HelloWorldProvider, [HelloWorldV1Marker,], AnyMarker); |
109 | | /// |
110 | | /// let req = DataRequest { |
111 | | /// locale: &icu_locid::langid!("de").into(), |
112 | | /// metadata: Default::default(), |
113 | | /// }; |
114 | | /// |
115 | | /// // Successful because the key matches: |
116 | | /// HelloWorldProvider.load_data(HelloWorldV1Marker::KEY, req).unwrap(); |
117 | | /// |
118 | | /// // MissingDataKey error as the key does not match: |
119 | | /// assert_eq!( |
120 | | /// HelloWorldProvider.load_data(icu_provider::data_key!("dummy@1"), req).unwrap_err().kind, |
121 | | /// DataErrorKind::MissingDataKey, |
122 | | /// ); |
123 | | /// ``` |
124 | | /// |
125 | | /// ## Wrapping DynamicDataProvider |
126 | | /// |
127 | | /// It is also possible to wrap a [`DynamicDataProvider`] to create another [`DynamicDataProvider`]. To do this, |
128 | | /// pass a match-like statement for keys as the second argument: |
129 | | /// |
130 | | /// ``` |
131 | | /// use icu_provider::prelude::*; |
132 | | /// use icu_provider::hello_world::*; |
133 | | /// # |
134 | | /// # struct HelloWorldProvider; |
135 | | /// # impl DynamicDataProvider<HelloWorldV1Marker> for HelloWorldProvider { |
136 | | /// # fn load_data(&self, key: DataKey, req: DataRequest) |
137 | | /// # -> Result<DataResponse<HelloWorldV1Marker>, DataError> { |
138 | | /// # icu_provider::hello_world::HelloWorldProvider.load(req) |
139 | | /// # } |
140 | | /// # } |
141 | | /// |
142 | | /// // Implement DataProvider<AnyMarker> on HelloWorldProvider: DynamicDataProvider<HelloWorldV1Marker> |
143 | | /// icu_provider::impl_dynamic_data_provider!(HelloWorldProvider, { |
144 | | /// // Match HelloWorldV1Marker::KEY and delegate to DynamicDataProvider<HelloWorldV1Marker>. |
145 | | /// HW = HelloWorldV1Marker::KEY => HelloWorldV1Marker, |
146 | | /// // Send the wildcard match also to DynamicDataProvider<HelloWorldV1Marker>. |
147 | | /// _ => HelloWorldV1Marker, |
148 | | /// }, AnyMarker); |
149 | | /// |
150 | | /// let req = DataRequest { |
151 | | /// locale: &icu_locid::langid!("de").into(), |
152 | | /// metadata: Default::default(), |
153 | | /// }; |
154 | | /// |
155 | | /// // Successful because the key matches: |
156 | | /// HelloWorldProvider.as_any_provider().load_any(HelloWorldV1Marker::KEY, req).unwrap(); |
157 | | /// |
158 | | /// // Because of the wildcard, any key actually works: |
159 | | /// HelloWorldProvider.as_any_provider().load_any(icu_provider::data_key!("dummy@1"), req).unwrap(); |
160 | | /// ``` |
161 | | /// |
162 | | /// [`DynamicDataProvider`]: crate::DynamicDataProvider |
163 | | /// [`DataProvider`]: crate::DataProvider |
164 | | /// [`AnyPayload`]: (crate::any::AnyPayload) |
165 | | /// [`DataErrorKind::MissingDataKey`]: (crate::DataErrorKind::MissingDataKey) |
166 | | /// [`SerializeMarker`]: (crate::serde::SerializeMarker) |
167 | | #[macro_export] |
168 | | macro_rules! impl_dynamic_data_provider { |
169 | | // allow passing in multiple things to do and get dispatched |
170 | | ($provider:ty, $arms:tt, $one:path, $($rest:path),+) => { |
171 | | $crate::impl_dynamic_data_provider!( |
172 | | $provider, |
173 | | $arms, |
174 | | $one |
175 | | ); |
176 | | |
177 | | $crate::impl_dynamic_data_provider!( |
178 | | $provider, |
179 | | $arms, |
180 | | $($rest),+ |
181 | | ); |
182 | | }; |
183 | | |
184 | | ($provider:ty, { $($ident:ident = $key:path => $struct_m:ty),+, $(_ => $struct_d:ty,)?}, $dyn_m:ty) => { |
185 | | impl $crate::DynamicDataProvider<$dyn_m> for $provider |
186 | | { |
187 | | fn load_data( |
188 | | &self, |
189 | | key: $crate::DataKey, |
190 | | req: $crate::DataRequest, |
191 | | ) -> Result< |
192 | | $crate::DataResponse<$dyn_m>, |
193 | | $crate::DataError, |
194 | | > { |
195 | | match key.hashed() { |
196 | | $( |
197 | | h if h == $key.hashed() => { |
198 | | let result: $crate::DataResponse<$struct_m> = |
199 | | $crate::DynamicDataProvider::<$struct_m>::load_data(self, key, req)?; |
200 | | Ok($crate::DataResponse { |
201 | | metadata: result.metadata, |
202 | | payload: result.payload.map(|p| { |
203 | | $crate::dynutil::UpcastDataPayload::<$struct_m>::upcast(p) |
204 | | }), |
205 | | }) |
206 | | } |
207 | | )+, |
208 | | $( |
209 | | _ => { |
210 | | let result: $crate::DataResponse<$struct_d> = |
211 | | $crate::DynamicDataProvider::<$struct_d>::load_data(self, key, req)?; |
212 | | Ok($crate::DataResponse { |
213 | | metadata: result.metadata, |
214 | | payload: result.payload.map(|p| { |
215 | | $crate::dynutil::UpcastDataPayload::<$struct_d>::upcast(p) |
216 | | }), |
217 | | }) |
218 | | } |
219 | | )? |
220 | | _ => Err($crate::DataErrorKind::MissingDataKey.with_req(key, req)) |
221 | | } |
222 | | } |
223 | | } |
224 | | |
225 | | }; |
226 | | ($provider:ty, [ $($(#[$cfg:meta])? $struct_m:ty),+, ], $dyn_m:path) => { |
227 | | impl $crate::DynamicDataProvider<$dyn_m> for $provider |
228 | | { |
229 | 0 | fn load_data( |
230 | 0 | &self, |
231 | 0 | key: $crate::DataKey, |
232 | 0 | req: $crate::DataRequest, |
233 | 0 | ) -> Result< |
234 | 0 | $crate::DataResponse<$dyn_m>, |
235 | 0 | $crate::DataError, |
236 | 0 | > { |
237 | 0 | match key.hashed() { |
238 | | $( |
239 | | $(#[$cfg])? |
240 | 0 | h if h == <$struct_m>::KEY.hashed() => { |
241 | 0 | let result: $crate::DataResponse<$struct_m> = |
242 | 0 | $crate::DataProvider::load(self, req)?; |
243 | | Ok($crate::DataResponse { |
244 | 0 | metadata: result.metadata, |
245 | 0 | payload: result.payload.map(|p| { |
246 | 0 | $crate::dynutil::UpcastDataPayload::<$struct_m>::upcast(p) |
247 | 0 | }), |
248 | | }) |
249 | | } |
250 | | )+, |
251 | 0 | _ => Err($crate::DataErrorKind::MissingDataKey.with_req(key, req)) |
252 | | } |
253 | 0 | } |
254 | | } |
255 | | }; |
256 | | } |