Coverage Report

Created: 2025-11-02 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}