Skip to main content

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