icu_provider/
response.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 crate::buf::BufferMarker;
6use crate::DataError;
7use crate::DataLocale;
8use crate::DynamicDataMarker;
9#[cfg(feature = "alloc")]
10use alloc::boxed::Box;
11use core::fmt::Debug;
12use core::marker::PhantomData;
13#[cfg(feature = "alloc")]
14use core::ops::Deref;
15use yoke::cartable_ptr::CartableOptionPointer;
16use yoke::*;
17
18#[cfg(feature = "alloc")]
19#[cfg(not(feature = "sync"))]
20use alloc::rc::Rc as SelectedRc;
21#[cfg(feature = "alloc")]
22#[cfg(feature = "sync")]
23use alloc::sync::Arc as SelectedRc;
24
25/// A response object containing metadata about the returned data.
26#[derive(#[automatically_derived]
impl ::core::fmt::Debug for DataResponseMetadata {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field3_finish(f,
            "DataResponseMetadata", "locale", &self.locale, "buffer_format",
            &self.buffer_format, "checksum", &&self.checksum)
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for DataResponseMetadata {
    #[inline]
    fn clone(&self) -> DataResponseMetadata {
        DataResponseMetadata {
            locale: ::core::clone::Clone::clone(&self.locale),
            buffer_format: ::core::clone::Clone::clone(&self.buffer_format),
            checksum: ::core::clone::Clone::clone(&self.checksum),
        }
    }
}Clone, #[automatically_derived]
impl ::core::cmp::PartialEq for DataResponseMetadata {
    #[inline]
    fn eq(&self, other: &DataResponseMetadata) -> bool {
        self.locale == other.locale &&
                self.buffer_format == other.buffer_format &&
            self.checksum == other.checksum
    }
}PartialEq, #[automatically_derived]
impl ::core::default::Default for DataResponseMetadata {
    #[inline]
    fn default() -> DataResponseMetadata {
        DataResponseMetadata {
            locale: ::core::default::Default::default(),
            buffer_format: ::core::default::Default::default(),
            checksum: ::core::default::Default::default(),
        }
    }
}Default)]
27#[non_exhaustive]
28pub struct DataResponseMetadata {
29    /// The resolved locale of the returned data, if locale fallbacking was performed.
30    pub locale: Option<DataLocale>,
31    /// The format of the buffer for buffer-backed data, if known (for example, JSON).
32    pub buffer_format: Option<crate::buf::BufferFormat>,
33    /// An optional checksum. This can be used to ensure consistency across different markers.
34    pub checksum: Option<u64>,
35}
36
37impl DataResponseMetadata {
38    /// Sets the checksum.
39    pub fn with_checksum(self, checksum: u64) -> Self {
40        Self {
41            checksum: Some(checksum),
42            ..self
43        }
44    }
45}
46
47/// A container for data payloads returned from a data provider.
48///
49/// [`DataPayload`] is built on top of the [`yoke`] framework, which allows for cheap, zero-copy
50/// operations on data via the use of self-references.
51///
52/// The type of the data stored in [`DataPayload`] is determined by the [`DynamicDataMarker`] type parameter.
53///
54/// ## Accessing the data
55///
56/// To get a reference to the data inside [`DataPayload`], use [`DataPayload::get()`]. If you need
57/// to store the data for later use, you need to store the [`DataPayload`] itself, since `get` only
58/// returns a reference with an ephemeral lifetime.
59///
60/// ## Mutating the data
61///
62/// To modify the data stored in a [`DataPayload`], use [`DataPayload::with_mut()`].
63///
64/// ## Transforming the data to a different type
65///
66/// To transform a [`DataPayload`] to a different type backed by the same data store (cart), use
67/// [`DataPayload::map_project()`] or one of its sister methods.
68///
69/// # Cargo feature: `sync`
70///
71/// By default, the payload uses non-concurrent reference counting internally, and hence is neither
72/// [`Sync`] nor [`Send`]; if these traits are required, the `sync` Cargo feature can be enabled.
73///
74/// # Examples
75///
76/// Basic usage, using the `HelloWorldV1` marker:
77///
78/// ```
79/// use icu_provider::hello_world::*;
80/// use icu_provider::prelude::*;
81/// use std::borrow::Cow;
82///
83/// let payload = DataPayload::<HelloWorldV1>::from_owned(HelloWorld {
84///     message: Cow::Borrowed("Demo"),
85/// });
86///
87/// assert_eq!("Demo", payload.get().message);
88/// ```
89pub struct DataPayload<M: DynamicDataMarker>(pub(crate) DataPayloadInner<M>);
90
91/// A container for data payloads with storage for something else.
92///
93/// The type parameter `O` is stored as part of the interior enum, leading to
94/// better stack size optimization. `O` can be as large as the [`DataPayload`]
95/// minus two words without impacting stack size.
96///
97/// # Examples
98///
99/// Create and use DataPayloadOr:
100///
101/// ```
102/// use icu_locale_core::langid;
103/// use icu_provider::hello_world::*;
104/// use icu_provider::prelude::*;
105/// use icu_provider::DataPayloadOr;
106///
107/// let response: DataResponse<HelloWorldV1> = HelloWorldProvider
108///     .load(DataRequest {
109///         id: DataIdentifierBorrowed::for_locale(&langid!("de").into()),
110///         ..Default::default()
111///     })
112///     .expect("Loading should succeed");
113///
114/// let payload_some =
115///     DataPayloadOr::<HelloWorldV1, ()>::from_payload(response.payload);
116/// let payload_none = DataPayloadOr::<HelloWorldV1, ()>::from_other(());
117///
118/// assert_eq!(
119///     payload_some.get(),
120///     Ok(&HelloWorld {
121///         message: "Hallo Welt".into()
122///     })
123/// );
124/// assert_eq!(payload_none.get(), Err(&()));
125/// ```
126///
127/// Stack size comparison:
128///
129/// ```
130/// use core::mem::size_of;
131/// use icu_provider::prelude::*;
132/// use icu_provider::DataPayloadOr;
133///
134/// const W: usize = size_of::<usize>();
135///
136/// // Data struct is 3 words:
137/// icu_provider::data_marker!(SampleV1, [usize; 3]);
138///
139/// // DataPayload adds a word for a total of 4 words:
140/// assert_eq!(W * 4, size_of::<DataPayload<SampleV1>>());
141///
142/// // Option<DataPayload> balloons to 5 words:
143/// assert_eq!(W * 5, size_of::<Option<DataPayload<SampleV1>>>());
144///
145/// // But, using DataPayloadOr is the same size as DataPayload:
146/// assert_eq!(W * 4, size_of::<DataPayloadOr<SampleV1, ()>>());
147///
148/// // The largest optimized Other type is two words smaller than the DataPayload:
149/// assert_eq!(W * 4, size_of::<DataPayloadOr<SampleV1, [usize; 1]>>());
150/// assert_eq!(W * 4, size_of::<DataPayloadOr<SampleV1, [usize; 2]>>());
151/// assert_eq!(W * 5, size_of::<DataPayloadOr<SampleV1, [usize; 3]>>());
152/// ```
153pub struct DataPayloadOr<M: DynamicDataMarker, O>(pub(crate) DataPayloadOrInner<M, O>);
154
155pub(crate) enum DataPayloadInner<M: DynamicDataMarker> {
156    Yoke(Yoke<M::DataStruct, CartableOptionPointer<CartInner>>),
157    StaticRef(&'static M::DataStruct),
158}
159
160pub(crate) enum DataPayloadOrInner<M: DynamicDataMarker, O> {
161    Yoke(Yoke<M::DataStruct, CartableOptionPointer<CartInner>>),
162    Inner(DataPayloadOrInnerInner<M, O>),
163}
164
165pub(crate) enum DataPayloadOrInnerInner<M: DynamicDataMarker, O> {
166    StaticRef(&'static M::DataStruct),
167    Other(O),
168}
169
170/// The type of the "cart" that is used by [`DataPayload`].
171///
172/// This type is public but the inner cart type is private. To create a
173/// [`Yoke`] with this cart, use [`Cart::try_make_yoke`]. Then, convert
174/// it to a [`DataPayload`] with [`DataPayload::from_yoked_buffer`].
175#[derive(#[automatically_derived]
impl ::core::clone::Clone for Cart {
    #[inline]
    fn clone(&self) -> Cart { Cart(::core::clone::Clone::clone(&self.0)) }
}Clone, #[automatically_derived]
impl ::core::fmt::Debug for Cart {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Cart", &&self.0)
    }
}Debug)]
176pub struct Cart(#[allow(dead_code)] CartInner);
177
178/// The actual cart type (private typedef).
179#[cfg(feature = "alloc")]
180pub(crate) type CartInner = SelectedRc<Box<[u8]>>;
181#[cfg(not(feature = "alloc"))]
182pub(crate) type CartInner = &'static ();
183
184// Safety: Rc, Arc, and () are CloneableCart, and our impl delegates.
185unsafe impl yoke::CloneableCart for Cart {}
186
187#[cfg(feature = "alloc")]
188impl Deref for Cart {
189    type Target = Box<[u8]>;
190    fn deref(&self) -> &Self::Target {
191        &self.0
192    }
193}
194// Safety: both Rc and Arc are StableDeref, and our impl delegates.
195#[cfg(feature = "alloc")]
196unsafe impl stable_deref_trait::StableDeref for Cart {}
197
198impl Cart {
199    #[cfg(feature = "alloc")]
200    /// Creates a `Yoke<Y, Option<Cart>>` from owned bytes by applying `f`.
201    ///
202    /// ✨ *Enabled with the `alloc` Cargo feature.*
203    pub fn try_make_yoke<Y, F, E>(cart: Box<[u8]>, f: F) -> Result<Yoke<Y, Option<Self>>, E>
204    where
205        for<'a> Y: Yokeable<'a>,
206        F: FnOnce(&[u8]) -> Result<<Y as Yokeable>::Output, E>,
207    {
208        Yoke::try_attach_to_cart(SelectedRc::new(cart), |b| f(b))
209            // Safety: The cart is only wrapped, no data is leaked
210            .map(|yoke| unsafe { yoke.replace_cart(Cart) })
211            .map(Yoke::wrap_cart_in_option)
212    }
213
214    /// Helper function to convert `Yoke<Y, Option<Cart>>` to `Yoke<Y, Option<CartInner>>`.
215    #[inline]
216    pub(crate) fn unwrap_cart<Y>(yoke: Yoke<Y, Option<Cart>>) -> Yoke<Y, Option<CartInner>>
217    where
218        for<'a> Y: Yokeable<'a>,
219    {
220        // Safety: `Cart` has one field and we are removing it from the newtype,
221        // and we are preserving it in the new cart, unwrapping it from the newtype.
222        unsafe { yoke.replace_cart(|option_cart| option_cart.map(|cart| cart.0)) }
223    }
224}
225
226impl<M> Debug for DataPayload<M>
227where
228    M: DynamicDataMarker,
229    for<'a> &'a <M::DataStruct as Yokeable<'a>>::Output: Debug,
230{
231    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
232        self.get().fmt(f)
233    }
234}
235
236impl<M, O> Debug for DataPayloadOr<M, O>
237where
238    M: DynamicDataMarker,
239    for<'a> &'a <M::DataStruct as Yokeable<'a>>::Output: Debug,
240    O: Debug,
241{
242    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
243        self.get()
244            .map(|v| Debug::fmt(&v, f))
245            .unwrap_or_else(|v| Debug::fmt(v, f))
246    }
247}
248
249/// Cloning a DataPayload is generally a cheap operation.
250/// See notes in the `Clone` impl for [`Yoke`].
251///
252/// # Examples
253///
254/// ```no_run
255/// use icu_provider::hello_world::*;
256/// use icu_provider::prelude::*;
257///
258/// let resp1: DataPayload<HelloWorldV1> = todo!();
259/// let resp2 = resp1.clone();
260/// ```
261impl<M> Clone for DataPayload<M>
262where
263    M: DynamicDataMarker,
264    for<'a> <M::DataStruct as Yokeable<'a>>::Output: Clone,
265{
266    fn clone(&self) -> Self {
267        Self(match &self.0 {
268            DataPayloadInner::Yoke(yoke) => DataPayloadInner::Yoke(yoke.clone()),
269            DataPayloadInner::StaticRef(r) => DataPayloadInner::StaticRef(*r),
270        })
271    }
272}
273
274impl<M, O> Clone for DataPayloadOr<M, O>
275where
276    M: DynamicDataMarker,
277    for<'a> <M::DataStruct as Yokeable<'a>>::Output: Clone,
278    O: Clone,
279{
280    fn clone(&self) -> Self {
281        Self(match &self.0 {
282            DataPayloadOrInner::Yoke(yoke) => DataPayloadOrInner::Yoke(yoke.clone()),
283            DataPayloadOrInner::Inner(DataPayloadOrInnerInner::StaticRef(r)) => {
284                DataPayloadOrInner::Inner(DataPayloadOrInnerInner::StaticRef(*r))
285            }
286            DataPayloadOrInner::Inner(DataPayloadOrInnerInner::Other(o)) => {
287                DataPayloadOrInner::Inner(DataPayloadOrInnerInner::Other(o.clone()))
288            }
289        })
290    }
291}
292
293impl<M> PartialEq for DataPayload<M>
294where
295    M: DynamicDataMarker,
296    for<'a> <M::DataStruct as Yokeable<'a>>::Output: PartialEq,
297{
298    fn eq(&self, other: &Self) -> bool {
299        self.get() == other.get()
300    }
301}
302impl<M, O> PartialEq for DataPayloadOr<M, O>
303where
304    M: DynamicDataMarker,
305    for<'a> <M::DataStruct as Yokeable<'a>>::Output: PartialEq,
306    O: Eq,
307{
308    fn eq(&self, other: &Self) -> bool {
309        match (self.get(), other.get()) {
310            (Ok(x), Ok(y)) => x == y,
311            (Err(x), Err(y)) => x == y,
312            _ => false,
313        }
314    }
315}
316
317impl<M> Eq for DataPayload<M>
318where
319    M: DynamicDataMarker,
320    for<'a> <M::DataStruct as Yokeable<'a>>::Output: Eq,
321{
322}
323
324impl<M, O> Eq for DataPayloadOr<M, O>
325where
326    M: DynamicDataMarker,
327    for<'a> <M::DataStruct as Yokeable<'a>>::Output: Eq,
328    O: Eq,
329{
330}
331
332#[test]
333fn test_clone_eq() {
334    use crate::hello_world::*;
335    let p1 = DataPayload::<HelloWorldV1>::from_static_str("Demo");
336    let p2 = p1.clone();
337    assert_eq!(p1, p2);
338
339    let p1 = DataPayloadOr::<HelloWorldV1, usize>::from_payload(p1);
340    let p2 = p1.clone();
341    assert_eq!(p1, p2);
342
343    let p3 = DataPayloadOr::<HelloWorldV1, usize>::from_other(555);
344    let p4 = p3.clone();
345    assert_eq!(p3, p4);
346
347    let p5 = DataPayloadOr::<HelloWorldV1, usize>::from_other(666);
348    assert_ne!(p3, p5);
349    assert_ne!(p4, p5);
350
351    assert_ne!(p1, p3);
352    assert_ne!(p1, p4);
353    assert_ne!(p1, p5);
354    assert_ne!(p2, p3);
355    assert_ne!(p2, p4);
356    assert_ne!(p2, p5);
357}
358
359impl<M> DataPayload<M>
360where
361    M: DynamicDataMarker,
362{
363    /// Convert a fully owned (`'static`) data struct into a DataPayload.
364    ///
365    /// This constructor creates `'static` payloads.
366    ///
367    /// # Examples
368    ///
369    /// ```
370    /// use icu_provider::hello_world::*;
371    /// use icu_provider::prelude::*;
372    /// use std::borrow::Cow;
373    ///
374    /// let local_struct = HelloWorld {
375    ///     message: Cow::Owned("example".to_owned()),
376    /// };
377    ///
378    /// let payload = DataPayload::<HelloWorldV1>::from_owned(local_struct.clone());
379    ///
380    /// assert_eq!(payload.get(), &local_struct);
381    /// ```
382    #[inline]
383    pub fn from_owned(data: M::DataStruct) -> Self {
384        Self(DataPayloadInner::Yoke(
385            Yoke::new_owned(data).convert_cart_into_option_pointer(),
386        ))
387    }
388
389    /// Construct a [`DataPayload`] from a static reference.
390    ///
391    /// This is mainly used by databake.
392    #[inline]
393    pub const fn from_static_ref(data: &'static M::DataStruct) -> Self {
394        Self(DataPayloadInner::StaticRef(data))
395    }
396
397    /// Mutate the data contained in this DataPayload.
398    ///
399    /// For safety, all mutation operations must take place within a helper function that cannot
400    /// borrow data from the surrounding context.
401    ///
402    /// # Examples
403    ///
404    /// Basic usage:
405    ///
406    /// ```
407    /// use icu_provider::hello_world::HelloWorldV1;
408    /// use icu_provider::prelude::*;
409    ///
410    /// let mut payload = DataPayload::<HelloWorldV1>::from_static_str("Hello");
411    ///
412    /// payload.with_mut(|s| s.message.to_mut().push_str(" World"));
413    ///
414    /// assert_eq!("Hello World", payload.get().message);
415    /// ```
416    ///
417    /// To transfer data from the context into the data struct, use the `move` keyword:
418    ///
419    /// ```
420    /// use icu_provider::hello_world::HelloWorldV1;
421    /// use icu_provider::prelude::*;
422    ///
423    /// let mut payload = DataPayload::<HelloWorldV1>::from_static_str("Hello");
424    ///
425    /// let suffix = " World";
426    /// payload.with_mut(move |s| s.message.to_mut().push_str(suffix));
427    ///
428    /// assert_eq!("Hello World", payload.get().message);
429    /// ```
430    pub fn with_mut<'a, F>(&'a mut self, f: F)
431    where
432        F: 'static + for<'b> FnOnce(&'b mut <M::DataStruct as Yokeable<'a>>::Output),
433        M::DataStruct: zerofrom::ZeroFrom<'static, M::DataStruct>,
434    {
435        if let DataPayloadInner::StaticRef(r) = self.0 {
436            self.0 = DataPayloadInner::Yoke(
437                Yoke::new_owned(zerofrom::ZeroFrom::zero_from(r))
438                    .convert_cart_into_option_pointer(),
439            );
440        }
441        match &mut self.0 {
442            DataPayloadInner::Yoke(yoke) => yoke.with_mut(f),
443            _ => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
444        }
445    }
446
447    /// Borrows the underlying data.
448    ///
449    /// This function should be used like `Deref` would normally be used. For more information on
450    /// why DataPayload cannot implement `Deref`, see the `yoke` crate.
451    ///
452    /// # Examples
453    ///
454    /// ```
455    /// use icu_provider::hello_world::HelloWorldV1;
456    /// use icu_provider::prelude::*;
457    ///
458    /// let payload = DataPayload::<HelloWorldV1>::from_static_str("Demo");
459    ///
460    /// assert_eq!("Demo", payload.get().message);
461    /// ```
462    #[inline]
463    pub fn get<'a>(&'a self) -> &'a <M::DataStruct as Yokeable<'a>>::Output {
464        match &self.0 {
465            DataPayloadInner::Yoke(yoke) => yoke.get(),
466            DataPayloadInner::StaticRef(r) => Yokeable::transform(*r),
467        }
468    }
469
470    /// Borrows the underlying data statically if possible.
471    ///
472    /// This will succeed if [`DataPayload`] is constructed with [`DataPayload::from_static_ref`], which is used by
473    /// baked providers.
474    #[inline]
475    pub fn get_static(&self) -> Option<&'static <M::DataStruct as Yokeable<'static>>::Output> {
476        match &self.0 {
477            DataPayloadInner::Yoke(_) => None,
478            DataPayloadInner::StaticRef(r) => Some(Yokeable::transform(*r)),
479        }
480    }
481
482    /// Maps `DataPayload<M>` to `DataPayload<M2>` by projecting it with [`Yoke::map_project`].
483    ///
484    /// This is accomplished by a function that takes `M`'s data type and returns `M2`'s data
485    /// type. The function takes a second argument which should be ignored. For more details,
486    /// see [`Yoke::map_project()`].
487    ///
488    /// The standard [`DataPayload::map_project()`] function moves `self` and cannot capture any
489    /// data from its context. Use one of the sister methods if you need these capabilities:
490    ///
491    /// - [`DataPayload::map_project_cloned()`] if you don't have ownership of `self`
492    /// - [`DataPayload::try_map_project()`] to bubble up an error
493    /// - [`DataPayload::try_map_project_cloned()`] to do both of the above
494    ///
495    /// # Examples
496    ///
497    /// Map from `HelloWorld` to a `Cow<str>` containing just the message:
498    ///
499    /// ```
500    /// use icu_provider::hello_world::*;
501    /// use icu_provider::prelude::*;
502    /// use std::borrow::Cow;
503    ///
504    /// // A custom marker type is required when using `map_project`. The DataStruct should be the
505    /// // target type, and the Cart should correspond to the type being transformed.
506    ///
507    /// struct HelloWorldV1MessageMarker;
508    /// impl DynamicDataMarker for HelloWorldV1MessageMarker {
509    ///     type DataStruct = Cow<'static, str>;
510    /// }
511    ///
512    /// let p1: DataPayload<HelloWorldV1> = DataPayload::from_owned(HelloWorld {
513    ///     message: Cow::Borrowed("Hello World"),
514    /// });
515    ///
516    /// assert_eq!("Hello World", p1.get().message);
517    ///
518    /// let p2: DataPayload<HelloWorldV1MessageMarker> = p1.map_project(|obj, _| obj.message);
519    ///
520    /// // Note: at this point, p1 has been moved.
521    /// assert_eq!("Hello World", p2.get());
522    /// ```
523    pub fn map_project<M2, F>(self, f: F) -> DataPayload<M2>
524    where
525        M2: DynamicDataMarker,
526        F: for<'a> FnOnce(
527            <M::DataStruct as Yokeable<'a>>::Output,
528            PhantomData<&'a ()>,
529        ) -> <M2::DataStruct as Yokeable<'a>>::Output,
530        M::DataStruct: zerofrom::ZeroFrom<'static, M::DataStruct>,
531    {
532        DataPayload(DataPayloadInner::Yoke(
533            match self.0 {
534                DataPayloadInner::Yoke(yoke) => yoke,
535                DataPayloadInner::StaticRef(r) => Yoke::new_owned(zerofrom::ZeroFrom::zero_from(r))
536                    .convert_cart_into_option_pointer(),
537            }
538            .map_project(f),
539        ))
540    }
541
542    /// Version of [`DataPayload::map_project()`] that borrows `self` instead of moving `self`.
543    ///
544    /// # Examples
545    ///
546    /// Same example as above, but this time, do not move out of `p1`:
547    ///
548    /// ```
549    /// // Same imports and definitions as above
550    /// # use icu_provider::hello_world::*;
551    /// # use icu_provider::prelude::*;
552    /// # use std::borrow::Cow;
553    /// # struct HelloWorldV1MessageMarker;
554    /// # impl DynamicDataMarker for HelloWorldV1MessageMarker {
555    /// #     type DataStruct = Cow<'static, str>;
556    /// # }
557    ///
558    /// let p1: DataPayload<HelloWorldV1> = DataPayload::from_owned(HelloWorld {
559    ///     message: Cow::Borrowed("Hello World"),
560    /// });
561    ///
562    /// assert_eq!("Hello World", p1.get().message);
563    ///
564    /// let p2: DataPayload<HelloWorldV1MessageMarker> =
565    ///     p1.map_project_cloned(|obj, _| obj.message.clone());
566    ///
567    /// // Note: p1 is still valid.
568    /// assert_eq!(p1.get().message, *p2.get());
569    /// ```
570    pub fn map_project_cloned<'this, M2, F>(&'this self, f: F) -> DataPayload<M2>
571    where
572        M2: DynamicDataMarker,
573        F: for<'a> FnOnce(
574            &'this <M::DataStruct as Yokeable<'a>>::Output,
575            PhantomData<&'a ()>,
576        ) -> <M2::DataStruct as Yokeable<'a>>::Output,
577    {
578        DataPayload(DataPayloadInner::Yoke(match &self.0 {
579            DataPayloadInner::Yoke(yoke) => yoke.map_project_cloned(f),
580            DataPayloadInner::StaticRef(r) => {
581                let output: <M2::DataStruct as Yokeable<'static>>::Output =
582                    f(Yokeable::transform(*r), PhantomData);
583                // Safety: <M2::Yokeable as Yokeable<'static>>::Output is the same type as M2::Yokeable;
584                // we're going from 'static to 'static, however in a generic context it's not
585                // clear to the compiler that that is the case. We have to use the unsafe make API to do this.
586                let yokeable: M2::DataStruct = unsafe { M2::DataStruct::make(output) };
587                Yoke::new_owned(yokeable).convert_cart_into_option_pointer()
588            }
589        }))
590    }
591
592    /// Version of [`DataPayload::map_project()`] that bubbles up an error from `f`.
593    ///
594    /// # Examples
595    ///
596    /// Same example as above, but bubble up an error:
597    ///
598    /// ```
599    /// // Same imports and definitions as above
600    /// # use icu_provider::hello_world::*;
601    /// # use icu_provider::prelude::*;
602    /// # use std::borrow::Cow;
603    /// # struct HelloWorldV1MessageMarker;
604    /// # impl DynamicDataMarker for HelloWorldV1MessageMarker {
605    /// #     type DataStruct = Cow<'static, str>;
606    /// # }
607    ///
608    /// let p1: DataPayload<HelloWorldV1> = DataPayload::from_owned(HelloWorld {
609    ///     message: Cow::Borrowed("Hello World"),
610    /// });
611    ///
612    /// assert_eq!("Hello World", p1.get().message);
613    ///
614    /// let string_to_append = "Extra";
615    /// let p2: DataPayload<HelloWorldV1MessageMarker> =
616    ///     p1.try_map_project(|mut obj, _| {
617    ///         if obj.message.is_empty() {
618    ///             return Err("Example error");
619    ///         }
620    ///         obj.message.to_mut().push_str(string_to_append);
621    ///         Ok(obj.message)
622    ///     })?;
623    ///
624    /// assert_eq!("Hello WorldExtra", p2.get());
625    /// # Ok::<(), &'static str>(())
626    /// ```
627    pub fn try_map_project<M2, F, E>(self, f: F) -> Result<DataPayload<M2>, E>
628    where
629        M2: DynamicDataMarker,
630        F: for<'a> FnOnce(
631            <M::DataStruct as Yokeable<'a>>::Output,
632            PhantomData<&'a ()>,
633        ) -> Result<<M2::DataStruct as Yokeable<'a>>::Output, E>,
634        M::DataStruct: zerofrom::ZeroFrom<'static, M::DataStruct>,
635    {
636        Ok(DataPayload(DataPayloadInner::Yoke(
637            match self.0 {
638                DataPayloadInner::Yoke(yoke) => yoke,
639                DataPayloadInner::StaticRef(r) => Yoke::new_owned(zerofrom::ZeroFrom::zero_from(r))
640                    .convert_cart_into_option_pointer(),
641            }
642            .try_map_project(f)?,
643        )))
644    }
645
646    /// Version of [`DataPayload::map_project_cloned()`] that  bubbles up an error from `f`.
647    ///
648    /// # Examples
649    ///
650    /// Same example as above, but bubble up an error:
651    ///
652    /// ```
653    /// // Same imports and definitions as above
654    /// # use icu_provider::hello_world::*;
655    /// # use icu_provider::prelude::*;
656    /// # use std::borrow::Cow;
657    /// # struct HelloWorldV1MessageMarker;
658    /// # impl DynamicDataMarker for HelloWorldV1MessageMarker {
659    /// #     type DataStruct = Cow<'static, str>;
660    /// # }
661    ///
662    /// let p1: DataPayload<HelloWorldV1> = DataPayload::from_owned(HelloWorld {
663    ///     message: Cow::Borrowed("Hello World"),
664    /// });
665    ///
666    /// assert_eq!("Hello World", p1.get().message);
667    ///
668    /// let string_to_append = "Extra";
669    /// let p2: DataPayload<HelloWorldV1MessageMarker> = p1
670    ///     .try_map_project_cloned(|obj, _| {
671    ///         if obj.message.is_empty() {
672    ///             return Err("Example error");
673    ///         }
674    ///         let mut message = obj.message.clone();
675    ///         message.to_mut().push_str(string_to_append);
676    ///         Ok(message)
677    ///     })?;
678    ///
679    /// // Note: p1 is still valid, but the values no longer equal.
680    /// assert_ne!(p1.get().message, *p2.get());
681    /// assert_eq!("Hello WorldExtra", p2.get());
682    /// # Ok::<(), &'static str>(())
683    /// ```
684    pub fn try_map_project_cloned<'this, M2, F, E>(&'this self, f: F) -> Result<DataPayload<M2>, E>
685    where
686        M2: DynamicDataMarker,
687        F: for<'a> FnOnce(
688            &'this <M::DataStruct as Yokeable<'a>>::Output,
689            PhantomData<&'a ()>,
690        ) -> Result<<M2::DataStruct as Yokeable<'a>>::Output, E>,
691    {
692        Ok(DataPayload(DataPayloadInner::Yoke(match &self.0 {
693            DataPayloadInner::Yoke(yoke) => yoke.try_map_project_cloned(f)?,
694            DataPayloadInner::StaticRef(r) => {
695                let output: <M2::DataStruct as Yokeable<'static>>::Output =
696                    f(Yokeable::transform(*r), PhantomData)?;
697                // Safety: <M2::Yokeable as Yokeable<'static>>::Output is the same type as M2::Yokeable,
698                // and `output` is `'static` so there are no lifetimes to manage for `make()`
699                Yoke::new_owned(unsafe { M2::DataStruct::make(output) })
700                    .convert_cart_into_option_pointer()
701            }
702        })))
703    }
704
705    /// Convert between two [`DynamicDataMarker`] types that are compatible with each other
706    /// with compile-time type checking.
707    ///
708    /// This happens if they both have the same [`DynamicDataMarker::DataStruct`] type.
709    ///
710    /// Can be used to erase the marker of a data payload in cases where multiple markers correspond
711    /// to the same data struct.
712    ///
713    /// For runtime dynamic casting, use [`DataPayload::dynamic_cast_mut()`].
714    ///
715    /// # Examples
716    ///
717    /// ```no_run
718    /// use icu_provider::hello_world::*;
719    /// use icu_provider::prelude::*;
720    ///
721    /// struct CustomHelloWorldV1;
722    /// impl DynamicDataMarker for CustomHelloWorldV1 {
723    ///     type DataStruct = HelloWorld<'static>;
724    /// }
725    ///
726    /// let hello_world: DataPayload<HelloWorldV1> = todo!();
727    /// let custom: DataPayload<CustomHelloWorldV1> = hello_world.cast();
728    /// ```
729    #[inline]
730    pub fn cast<M2>(self) -> DataPayload<M2>
731    where
732        M2: DynamicDataMarker<DataStruct = M::DataStruct>,
733    {
734        DataPayload(match self.0 {
735            DataPayloadInner::Yoke(yoke) => DataPayloadInner::Yoke(yoke),
736            DataPayloadInner::StaticRef(r) => DataPayloadInner::StaticRef(r),
737        })
738    }
739
740    /// Convert between two [`DynamicDataMarker`] types that are compatible with each other
741    /// with compile-time type checking.
742    ///
743    /// This happens if they both have the same [`DynamicDataMarker::DataStruct`] type.
744    ///
745    /// Can be used to erase the marker of a data payload in cases where multiple markers correspond
746    /// to the same data struct.
747    #[inline]
748    pub fn cast_ref<M2>(&self) -> &DataPayload<M2>
749    where
750        M2: DynamicDataMarker<DataStruct = M::DataStruct>,
751    {
752        // SAFETY: As seen in the implementation of `cast`, the struct is the same, it's just the generic that changes.
753        unsafe { core::mem::transmute(self) }
754    }
755
756    /// Convert a [`DataPayload`] to one of the same type with runtime type checking.
757    ///
758    /// Primarily useful to convert from a generic to a concrete marker type.
759    ///
760    /// If the `M2` type argument does not match the true marker type, a `DataError` is returned.
761    ///
762    /// For compile-time static casting, use [`DataPayload::cast()`].
763    ///
764    /// # Examples
765    ///
766    /// Short-circuit a data request request based on the marker, returning
767    /// a result from a different data provider:
768    ///
769    /// ```
770    /// use core::any::TypeId;
771    /// use icu_locale_core::locale;
772    /// use icu_provider::hello_world::*;
773    /// use icu_provider::prelude::*;
774    /// use icu_provider_adapters::empty::EmptyDataProvider;
775    /// use std::borrow::Cow;
776    ///
777    /// struct MyForkingProvider<P0, P1> {
778    ///     fallback_provider: P0,
779    ///     hello_world_provider: P1,
780    /// }
781    ///
782    /// impl<M, P0, P1> DataProvider<M> for MyForkingProvider<P0, P1>
783    /// where
784    ///     M: DataMarker,
785    ///     P0: DataProvider<M>,
786    ///     P1: DataProvider<HelloWorldV1>,
787    /// {
788    ///     #[inline]
789    ///     fn load(&self, req: DataRequest) -> Result<DataResponse<M>, DataError> {
790    ///         if TypeId::of::<HelloWorldV1>() == TypeId::of::<M>() {
791    ///             let response = DataProvider::<HelloWorldV1>::load(
792    ///                 &self.hello_world_provider,
793    ///                 req,
794    ///             )?;
795    ///             Ok(DataResponse {
796    ///                 metadata: response.metadata,
797    ///                 payload: response.payload.dynamic_cast()?,
798    ///             })
799    ///         } else {
800    ///             self.fallback_provider.load(req)
801    ///         }
802    ///     }
803    /// }
804    ///
805    /// let provider = MyForkingProvider {
806    ///     fallback_provider: EmptyDataProvider::new(),
807    ///     hello_world_provider: HelloWorldProvider,
808    /// };
809    ///
810    /// let formatter =
811    ///     HelloWorldFormatter::try_new_unstable(&provider, locale!("de").into())
812    ///         .unwrap();
813    ///
814    /// // This succeeds because the data was loaded from HelloWorldProvider
815    /// // rather than the empty fallback provider.
816    /// assert_eq!(formatter.format_to_string(), "Hallo Welt");
817    /// ```
818    pub fn dynamic_cast<M2>(self) -> Result<DataPayload<M2>, DataError>
819    where
820        M2: DynamicDataMarker,
821    {
822        let mut option_self = Some(self);
823        let mut option_out = None::<DataPayload<M2>>;
824        if let Some(x) = (&mut option_out as &mut dyn core::any::Any).downcast_mut() {
825            core::mem::swap(&mut option_self, x);
826            if true {
    if !option_out.is_some() {
        ::core::panicking::panic("assertion failed: option_out.is_some()")
    };
};debug_assert!(option_out.is_some());
827            if let Some(out) = option_out {
828                return Ok(out);
829            }
830        }
831        Err(DataError::for_type::<M2>().with_str_context(core::any::type_name::<M>()))
832    }
833
834    /// Convert a mutable reference of a [`DataPayload`] to another mutable reference
835    /// of the same type with runtime type checking.
836    ///
837    /// Primarily useful to convert from a generic to a concrete marker type.
838    ///
839    /// If the `M2` type argument does not match the true marker type, a `DataError` is returned.
840    ///
841    /// For compile-time static casting, use [`DataPayload::cast()`].
842    ///
843    /// # Examples
844    ///
845    /// Change the results of a particular request based on marker:
846    ///
847    /// ```
848    /// use icu_locale_core::locale;
849    /// use icu_provider::hello_world::*;
850    /// use icu_provider::prelude::*;
851    ///
852    /// struct MyWrapper<P> {
853    ///     inner: P,
854    /// }
855    ///
856    /// impl<M, P> DataProvider<M> for MyWrapper<P>
857    /// where
858    ///     M: DataMarker,
859    ///     P: DataProvider<M>,
860    /// {
861    ///     #[inline]
862    ///     fn load(&self, req: DataRequest) -> Result<DataResponse<M>, DataError> {
863    ///         let mut res = self.inner.load(req)?;
864    ///         let mut cast_result =
865    ///             res.payload.dynamic_cast_mut::<HelloWorldV1>();
866    ///         if let Ok(ref mut concrete_payload) = cast_result {
867    ///             // Add an emoji to the hello world message
868    ///             concrete_payload.with_mut(|data| {
869    ///                 data.message.to_mut().insert_str(0, "✨ ");
870    ///             });
871    ///         }
872    ///         Ok(res)
873    ///     }
874    /// }
875    ///
876    /// let provider = MyWrapper {
877    ///     inner: HelloWorldProvider,
878    /// };
879    /// let formatter =
880    ///     HelloWorldFormatter::try_new_unstable(&provider, locale!("de").into())
881    ///         .unwrap();
882    ///
883    /// assert_eq!(formatter.format_to_string(), "✨ Hallo Welt");
884    /// ```
885    #[inline]
886    pub fn dynamic_cast_mut<M2>(&mut self) -> Result<&mut DataPayload<M2>, DataError>
887    where
888        M2: DynamicDataMarker,
889    {
890        let this: &mut dyn core::any::Any = self;
891        if let Some(this) = this.downcast_mut() {
892            Ok(this)
893        } else {
894            Err(DataError::for_type::<M2>().with_str_context(core::any::type_name::<M>()))
895        }
896    }
897}
898
899impl DataPayload<BufferMarker> {
900    /// Converts an owned byte buffer into a `DataPayload<BufferMarker>`.
901    ///
902    /// ✨ *Enabled with the `alloc` Cargo feature.*
903    #[cfg(feature = "alloc")]
904    pub fn from_owned_buffer(buffer: Box<[u8]>) -> Self {
905        let yoke = Yoke::attach_to_cart(SelectedRc::new(buffer), |b| &**b)
906            .wrap_cart_in_option()
907            .convert_cart_into_option_pointer();
908        Self(DataPayloadInner::Yoke(yoke))
909    }
910
911    /// Converts a yoked byte buffer into a `DataPayload<BufferMarker>`.
912    pub fn from_yoked_buffer(yoke: Yoke<&'static [u8], Option<Cart>>) -> Self {
913        let yoke = Cart::unwrap_cart(yoke);
914        Self(DataPayloadInner::Yoke(
915            yoke.convert_cart_into_option_pointer(),
916        ))
917    }
918
919    /// Converts a static byte buffer into a `DataPayload<BufferMarker>`.
920    pub fn from_static_buffer(buffer: &'static [u8]) -> Self {
921        Self(DataPayloadInner::Yoke(
922            Yoke::new_owned(buffer).convert_cart_into_option_pointer(),
923        ))
924    }
925}
926
927impl<M> Default for DataPayload<M>
928where
929    M: DynamicDataMarker,
930    M::DataStruct: Default,
931{
932    fn default() -> Self {
933        Self::from_owned(Default::default())
934    }
935}
936
937impl<M, O> DataPayloadOr<M, O>
938where
939    M: DynamicDataMarker,
940{
941    /// Creates a [`DataPayloadOr`] from a [`DataPayload`].
942    #[inline]
943    pub fn from_payload(payload: DataPayload<M>) -> Self {
944        match payload.0 {
945            DataPayloadInner::Yoke(yoke) => Self(DataPayloadOrInner::Yoke(yoke)),
946            DataPayloadInner::StaticRef(r) => Self(DataPayloadOrInner::Inner(
947                DataPayloadOrInnerInner::StaticRef(r),
948            )),
949        }
950    }
951
952    /// Creates a [`DataPayloadOr`] from the other type `O`.
953    #[inline]
954    pub fn from_other(other: O) -> Self {
955        Self(DataPayloadOrInner::Inner(DataPayloadOrInnerInner::Other(
956            other,
957        )))
958    }
959
960    /// Returns whether this object represents a [`DataPayload`].
961    #[inline]
962    pub fn is_payload(&self) -> bool {
963        match &self.0 {
964            DataPayloadOrInner::Yoke(_) => true,
965            DataPayloadOrInner::Inner(DataPayloadOrInnerInner::StaticRef(_)) => true,
966            DataPayloadOrInner::Inner(DataPayloadOrInnerInner::Other(_)) => false,
967        }
968    }
969
970    /// Gets the value from this [`DataPayload`] as `Ok` or the other type as `Err`.
971    #[inline]
972    pub fn get<'a>(&'a self) -> Result<&'a <M::DataStruct as Yokeable<'a>>::Output, &'a O> {
973        match &self.0 {
974            DataPayloadOrInner::Yoke(yoke) => Ok(yoke.get()),
975            DataPayloadOrInner::Inner(DataPayloadOrInnerInner::StaticRef(r)) => {
976                Ok(Yokeable::transform(*r))
977            }
978            DataPayloadOrInner::Inner(DataPayloadOrInnerInner::Other(o)) => Err(o),
979        }
980    }
981
982    /// Consumes this [`DataPayloadOr`], returning either the wrapped
983    /// [`DataPayload`] or the other type.
984    #[inline]
985    pub fn into_inner(self) -> Result<DataPayload<M>, O> {
986        match self.0 {
987            DataPayloadOrInner::Yoke(yoke) => Ok(DataPayload(DataPayloadInner::Yoke(yoke))),
988            DataPayloadOrInner::Inner(DataPayloadOrInnerInner::StaticRef(r)) => {
989                Ok(DataPayload(DataPayloadInner::StaticRef(r)))
990            }
991            DataPayloadOrInner::Inner(DataPayloadOrInnerInner::Other(o)) => Err(o),
992        }
993    }
994}
995
996impl<M> DataPayloadOr<M, ()>
997where
998    M: DynamicDataMarker,
999{
1000    /// Convenience function to return the other type with value `()`
1001    #[inline]
1002    pub fn none() -> Self {
1003        Self::from_other(())
1004    }
1005
1006    /// Convenience function to return `Some` or `None` for other type `()`
1007    #[inline]
1008    pub fn get_option<'a>(&'a self) -> Option<&'a <M::DataStruct as Yokeable<'a>>::Output> {
1009        self.get().ok()
1010    }
1011}
1012
1013/// A response object containing an object as payload and metadata about it.
1014#[allow(clippy::exhaustive_structs)] // this type is stable
1015pub struct DataResponse<M>
1016where
1017    M: DynamicDataMarker,
1018{
1019    /// Metadata about the returned object.
1020    pub metadata: DataResponseMetadata,
1021
1022    /// The object itself
1023    pub payload: DataPayload<M>,
1024}
1025
1026impl<M> DataResponse<M>
1027where
1028    M: DynamicDataMarker,
1029{
1030    /// Convert between two [`DynamicDataMarker`] types that are compatible with each other
1031    /// with compile-time type checking.
1032    ///
1033    /// This happens if they both have the same [`DynamicDataMarker::DataStruct`] type.
1034    ///
1035    /// Can be used to erase the marker of a data payload in cases where multiple markers correspond
1036    /// to the same data struct.
1037    ///
1038    /// For runtime dynamic casting, use [`DataResponse::dynamic_cast()`].
1039    #[inline]
1040    pub fn cast<M2>(self) -> DataResponse<M2>
1041    where
1042        M2: DynamicDataMarker<DataStruct = M::DataStruct>,
1043    {
1044        DataResponse {
1045            metadata: self.metadata,
1046            payload: self.payload.cast(),
1047        }
1048    }
1049
1050    /// Convert a [`DataResponse`] to one of the same type with runtime type checking.
1051    ///
1052    /// Primarily useful to convert from a generic to a concrete marker type.
1053    ///
1054    /// If the `M2` type argument does not match the true marker type, a `DataError` is returned.
1055    ///
1056    /// For compile-time static casting, use [`DataResponse::cast()`].
1057    #[inline]
1058    pub fn dynamic_cast<M2>(self) -> Result<DataResponse<M2>, DataError>
1059    where
1060        M2: DynamicDataMarker,
1061    {
1062        Ok(DataResponse {
1063            metadata: self.metadata,
1064            payload: self.payload.dynamic_cast()?,
1065        })
1066    }
1067}
1068
1069impl<M> Debug for DataResponse<M>
1070where
1071    M: DynamicDataMarker,
1072    for<'a> &'a <M::DataStruct as Yokeable<'a>>::Output: Debug,
1073{
1074    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1075        f.write_fmt(format_args!("DataResponse {{ metadata: {0:?}, payload: {1:?} }}",
        self.metadata, self.payload))write!(
1076            f,
1077            "DataResponse {{ metadata: {:?}, payload: {:?} }}",
1078            self.metadata, self.payload
1079        )
1080    }
1081}
1082
1083/// Cloning a DataResponse is generally a cheap operation.
1084/// See notes in the `Clone` impl for [`Yoke`].
1085///
1086/// # Examples
1087///
1088/// ```no_run
1089/// use icu_provider::hello_world::*;
1090/// use icu_provider::prelude::*;
1091///
1092/// let resp1: DataResponse<HelloWorldV1> = todo!();
1093/// let resp2 = resp1.clone();
1094/// ```
1095impl<M> Clone for DataResponse<M>
1096where
1097    M: DynamicDataMarker,
1098    for<'a> <M::DataStruct as Yokeable<'a>>::Output: Clone,
1099{
1100    fn clone(&self) -> Self {
1101        Self {
1102            metadata: self.metadata.clone(),
1103            payload: self.payload.clone(),
1104        }
1105    }
1106}
1107
1108#[test]
1109fn test_debug() {
1110    use crate::hello_world::*;
1111    use crate::prelude::*;
1112    let resp = HelloWorldProvider
1113        .load(DataRequest {
1114            id: DataIdentifierBorrowed::for_locale(&icu_locale_core::locale!("en").into()),
1115            ..Default::default()
1116        })
1117        .unwrap();
1118    assert_eq!("DataResponse { metadata: DataResponseMetadata { locale: None, buffer_format: None, checksum: Some(1234) }, payload: HelloWorld { message: \"Hello World\" } }", format!("{resp:?}"));
1119}