| 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 | fn load_data( |
| 230 | &self, |
| 231 | key: $crate::DataKey, |
| 232 | req: $crate::DataRequest, |
| 233 | ) -> Result< |
| 234 | $crate::DataResponse<$dyn_m>, |
| 235 | $crate::DataError, |
| 236 | > { |
| 237 | match key.hashed() { |
| 238 | $( |
| 239 | $(#[$cfg])? |
| 240 | h if h == <$struct_m>::KEY.hashed() => { |
| 241 | let result: $crate::DataResponse<$struct_m> = |
| 242 | $crate::DataProvider::load(self, req)?; |
| 243 | Ok($crate::DataResponse { |
| 244 | metadata: result.metadata, |
| 245 | payload: result.payload.map(|p| { |
| 246 | $crate::dynutil::UpcastDataPayload::<$struct_m>::upcast(p) |
| 247 | }), |
| 248 | }) |
| 249 | } |
| 250 | )+, |
| 251 | _ => Err($crate::DataErrorKind::MissingDataKey.with_req(key, req)) |
| 252 | } |
| 253 | } |
| 254 | } |
| 255 | }; |
| 256 | } |
| 257 | |