Struct DataPayload

struct DataPayload<M: DynamicDataMarker>(_)

A container for data payloads returned from a data provider.

DataPayload is built on top of the yoke framework, which allows for cheap, zero-copy operations on data via the use of self-references.

The type of the data stored in DataPayload is determined by the DynamicDataMarker type parameter.

Accessing the data

To get a reference to the data inside DataPayload, use [DataPayload::get()]. If you need to store the data for later use, you need to store the DataPayload itself, since get only returns a reference with an ephemeral lifetime.

Mutating the data

To modify the data stored in a DataPayload, use [DataPayload::with_mut()].

Transforming the data to a different type

To transform a DataPayload to a different type backed by the same data store (cart), use [DataPayload::map_project()] or one of its sister methods.

Cargo feature: sync

By default, the payload uses non-concurrent reference counting internally, and hence is neither Sync nor Send; if these traits are required, the sync Cargo feature can be enabled.

Examples

Basic usage, using the HelloWorldV1 marker:

use icu_provider::hello_world::*;
use icu_provider::prelude::*;
use std::borrow::Cow;

let payload = DataPayload::<HelloWorldV1>::from_owned(HelloWorld {
    message: Cow::Borrowed("Demo"),
});

assert_eq!("Demo", payload.get().message);

Implementations

impl DataPayload<BufferMarker>

fn from_yoked_buffer(yoke: Yoke<&'static [u8], Option<Cart>>) -> Self

Converts a yoked byte buffer into a DataPayload<BufferMarker>.

fn from_static_buffer(buffer: &'static [u8]) -> Self

Converts a static byte buffer into a DataPayload<BufferMarker>.

impl<M> DataPayload<M>

fn from_owned(data: <M as >::DataStruct) -> Self

Convert a fully owned ('static) data struct into a DataPayload.

This constructor creates 'static payloads.

Examples

use icu_provider::hello_world::*;
use icu_provider::prelude::*;
use std::borrow::Cow;

let local_struct = HelloWorld {
    message: Cow::Owned("example".to_owned()),
};

let payload = DataPayload::<HelloWorldV1>::from_owned(local_struct.clone());

assert_eq!(payload.get(), &local_struct);
const fn from_static_ref(data: &'static <M as >::DataStruct) -> Self

Construct a DataPayload from a static reference.

This is mainly used by databake.

fn with_mut<'a, F>(self: &'a mut Self, f: F)
where
    F: 'static + for<'b> FnOnce(&'b mut <<M as >::DataStruct as Yokeable<'a>>::Output),
    <M as >::DataStruct: ZeroFrom<'static, <M as >::DataStruct>

Mutate the data contained in this DataPayload.

For safety, all mutation operations must take place within a helper function that cannot borrow data from the surrounding context.

Examples

Basic usage:

use icu_provider::hello_world::HelloWorldV1;
use icu_provider::prelude::*;

let mut payload = DataPayload::<HelloWorldV1>::from_static_str("Hello");

payload.with_mut(|s| s.message.to_mut().push_str(" World"));

assert_eq!("Hello World", payload.get().message);

To transfer data from the context into the data struct, use the move keyword:

use icu_provider::hello_world::HelloWorldV1;
use icu_provider::prelude::*;

let mut payload = DataPayload::<HelloWorldV1>::from_static_str("Hello");

let suffix = " World";
payload.with_mut(move |s| s.message.to_mut().push_str(suffix));

assert_eq!("Hello World", payload.get().message);
fn get<'a>(self: &'a Self) -> &'a <<M as >::DataStruct as Yokeable<'a>>::Output

Borrows the underlying data.

This function should be used like Deref would normally be used. For more information on why DataPayload cannot implement Deref, see the yoke crate.

Examples

use icu_provider::hello_world::HelloWorldV1;
use icu_provider::prelude::*;

let payload = DataPayload::<HelloWorldV1>::from_static_str("Demo");

assert_eq!("Demo", payload.get().message);
fn get_static(self: &Self) -> Option<&'static <<M as >::DataStruct as Yokeable<'static>>::Output>

Borrows the underlying data statically if possible.

This will succeed if DataPayload is constructed with DataPayload::from_static_ref, which is used by baked providers.

fn map_project<M2, F>(self: Self, f: F) -> DataPayload<M2>
where
    M2: DynamicDataMarker,
    F: for<'a> FnOnce(<<M as >::DataStruct as Yokeable<'a>>::Output, PhantomData<&'a ()>) -> <<M2 as >::DataStruct as Yokeable<'a>>::Output,
    <M as >::DataStruct: ZeroFrom<'static, <M as >::DataStruct>

Maps DataPayload<M> to DataPayload<M2> by projecting it with Yoke::map_project.

This is accomplished by a function that takes M's data type and returns M2's data type. The function takes a second argument which should be ignored. For more details, see [Yoke::map_project()].

The standard [DataPayload::map_project()] function moves self and cannot capture any data from its context. Use one of the sister methods if you need these capabilities:

  • [DataPayload::map_project_cloned()] if you don't have ownership of self
  • [DataPayload::try_map_project()] to bubble up an error
  • [DataPayload::try_map_project_cloned()] to do both of the above

Examples

Map from HelloWorld to a Cow<str> containing just the message:

use icu_provider::hello_world::*;
use icu_provider::prelude::*;
use std::borrow::Cow;

// A custom marker type is required when using `map_project`. The DataStruct should be the
// target type, and the Cart should correspond to the type being transformed.

struct HelloWorldV1MessageMarker;
impl DynamicDataMarker for HelloWorldV1MessageMarker {
    type DataStruct = Cow<'static, str>;
}

let p1: DataPayload<HelloWorldV1> = DataPayload::from_owned(HelloWorld {
    message: Cow::Borrowed("Hello World"),
});

assert_eq!("Hello World", p1.get().message);

let p2: DataPayload<HelloWorldV1MessageMarker> = p1.map_project(|obj, _| obj.message);

// Note: at this point, p1 has been moved.
assert_eq!("Hello World", p2.get());
fn map_project_cloned<'this, M2, F>(self: &'this Self, f: F) -> DataPayload<M2>
where
    M2: DynamicDataMarker,
    F: for<'a> FnOnce(&'this <<M as >::DataStruct as Yokeable<'a>>::Output, PhantomData<&'a ()>) -> <<M2 as >::DataStruct as Yokeable<'a>>::Output

Version of [DataPayload::map_project()] that borrows self instead of moving self.

Examples

Same example as above, but this time, do not move out of p1:

// Same imports and definitions as above
# use icu_provider::hello_world::*;
# use icu_provider::prelude::*;
# use std::borrow::Cow;
# struct HelloWorldV1MessageMarker;
# impl DynamicDataMarker for HelloWorldV1MessageMarker {
#     type DataStruct = Cow<'static, str>;
# }

let p1: DataPayload<HelloWorldV1> = DataPayload::from_owned(HelloWorld {
    message: Cow::Borrowed("Hello World"),
});

assert_eq!("Hello World", p1.get().message);

let p2: DataPayload<HelloWorldV1MessageMarker> =
    p1.map_project_cloned(|obj, _| obj.message.clone());

// Note: p1 is still valid.
assert_eq!(p1.get().message, *p2.get());
fn try_map_project<M2, F, E>(self: Self, f: F) -> Result<DataPayload<M2>, E>
where
    M2: DynamicDataMarker,
    F: for<'a> FnOnce(<<M as >::DataStruct as Yokeable<'a>>::Output, PhantomData<&'a ()>) -> Result<<<M2 as >::DataStruct as Yokeable<'a>>::Output, E>,
    <M as >::DataStruct: ZeroFrom<'static, <M as >::DataStruct>

Version of [DataPayload::map_project()] that bubbles up an error from f.

Examples

Same example as above, but bubble up an error:

// Same imports and definitions as above
# use icu_provider::hello_world::*;
# use icu_provider::prelude::*;
# use std::borrow::Cow;
# struct HelloWorldV1MessageMarker;
# impl DynamicDataMarker for HelloWorldV1MessageMarker {
#     type DataStruct = Cow<'static, str>;
# }

let p1: DataPayload<HelloWorldV1> = DataPayload::from_owned(HelloWorld {
    message: Cow::Borrowed("Hello World"),
});

assert_eq!("Hello World", p1.get().message);

let string_to_append = "Extra";
let p2: DataPayload<HelloWorldV1MessageMarker> =
    p1.try_map_project(|mut obj, _| {
        if obj.message.is_empty() {
            return Err("Example error");
        }
        obj.message.to_mut().push_str(string_to_append);
        Ok(obj.message)
    })?;

assert_eq!("Hello WorldExtra", p2.get());
# Ok::<(), &'static str>(())
fn try_map_project_cloned<'this, M2, F, E>(self: &'this Self, f: F) -> Result<DataPayload<M2>, E>
where
    M2: DynamicDataMarker,
    F: for<'a> FnOnce(&'this <<M as >::DataStruct as Yokeable<'a>>::Output, PhantomData<&'a ()>) -> Result<<<M2 as >::DataStruct as Yokeable<'a>>::Output, E>

Version of [DataPayload::map_project_cloned()] that bubbles up an error from f.

Examples

Same example as above, but bubble up an error:

// Same imports and definitions as above
# use icu_provider::hello_world::*;
# use icu_provider::prelude::*;
# use std::borrow::Cow;
# struct HelloWorldV1MessageMarker;
# impl DynamicDataMarker for HelloWorldV1MessageMarker {
#     type DataStruct = Cow<'static, str>;
# }

let p1: DataPayload<HelloWorldV1> = DataPayload::from_owned(HelloWorld {
    message: Cow::Borrowed("Hello World"),
});

assert_eq!("Hello World", p1.get().message);

let string_to_append = "Extra";
let p2: DataPayload<HelloWorldV1MessageMarker> = p1
    .try_map_project_cloned(|obj, _| {
        if obj.message.is_empty() {
            return Err("Example error");
        }
        let mut message = obj.message.clone();
        message.to_mut().push_str(string_to_append);
        Ok(message)
    })?;

// Note: p1 is still valid, but the values no longer equal.
assert_ne!(p1.get().message, *p2.get());
assert_eq!("Hello WorldExtra", p2.get());
# Ok::<(), &'static str>(())
fn cast<M2>(self: Self) -> DataPayload<M2>
where
    M2: DynamicDataMarker<DataStruct = <M as >::DataStruct>

Convert between two DynamicDataMarker types that are compatible with each other with compile-time type checking.

This happens if they both have the same DynamicDataMarker::DataStruct type.

Can be used to erase the marker of a data payload in cases where multiple markers correspond to the same data struct.

For runtime dynamic casting, use [DataPayload::dynamic_cast_mut()].

Examples

use icu_provider::hello_world::*;
use icu_provider::prelude::*;

struct CustomHelloWorldV1;
impl DynamicDataMarker for CustomHelloWorldV1 {
    type DataStruct = HelloWorld<'static>;
}

let hello_world: DataPayload<HelloWorldV1> = todo!();
let custom: DataPayload<CustomHelloWorldV1> = hello_world.cast();
fn cast_ref<M2>(self: &Self) -> &DataPayload<M2>
where
    M2: DynamicDataMarker<DataStruct = <M as >::DataStruct>

Convert between two DynamicDataMarker types that are compatible with each other with compile-time type checking.

This happens if they both have the same DynamicDataMarker::DataStruct type.

Can be used to erase the marker of a data payload in cases where multiple markers correspond to the same data struct.

fn dynamic_cast<M2>(self: Self) -> Result<DataPayload<M2>, DataError>
where
    M2: DynamicDataMarker

Convert a DataPayload to one of the same type with runtime type checking.

Primarily useful to convert from a generic to a concrete marker type.

If the M2 type argument does not match the true marker type, a DataError is returned.

For compile-time static casting, use [DataPayload::cast()].

Examples

Short-circuit a data request request based on the marker, returning a result from a different data provider:

use core::any::TypeId;
use icu_locale_core::locale;
use icu_provider::hello_world::*;
use icu_provider::prelude::*;
use icu_provider_adapters::empty::EmptyDataProvider;
use std::borrow::Cow;

struct MyForkingProvider<P0, P1> {
    fallback_provider: P0,
    hello_world_provider: P1,
}

impl<M, P0, P1> DataProvider<M> for MyForkingProvider<P0, P1>
where
    M: DataMarker,
    P0: DataProvider<M>,
    P1: DataProvider<HelloWorldV1>,
{
    #[inline]
    fn load(&self, req: DataRequest) -> Result<DataResponse<M>, DataError> {
        if TypeId::of::<HelloWorldV1>() == TypeId::of::<M>() {
            let response = DataProvider::<HelloWorldV1>::load(
                &self.hello_world_provider,
                req,
            )?;
            Ok(DataResponse {
                metadata: response.metadata,
                payload: response.payload.dynamic_cast()?,
            })
        } else {
            self.fallback_provider.load(req)
        }
    }
}

let provider = MyForkingProvider {
    fallback_provider: EmptyDataProvider::new(),
    hello_world_provider: HelloWorldProvider,
};

let formatter =
    HelloWorldFormatter::try_new_unstable(&provider, locale!("de").into())
        .unwrap();

// This succeeds because the data was loaded from HelloWorldProvider
// rather than the empty fallback provider.
assert_eq!(formatter.format_to_string(), "Hallo Welt");
fn dynamic_cast_mut<M2>(self: &mut Self) -> Result<&mut DataPayload<M2>, DataError>
where
    M2: DynamicDataMarker

Convert a mutable reference of a DataPayload to another mutable reference of the same type with runtime type checking.

Primarily useful to convert from a generic to a concrete marker type.

If the M2 type argument does not match the true marker type, a DataError is returned.

For compile-time static casting, use [DataPayload::cast()].

Examples

Change the results of a particular request based on marker:

use icu_locale_core::locale;
use icu_provider::hello_world::*;
use icu_provider::prelude::*;

struct MyWrapper<P> {
    inner: P,
}

impl<M, P> DataProvider<M> for MyWrapper<P>
where
    M: DataMarker,
    P: DataProvider<M>,
{
    #[inline]
    fn load(&self, req: DataRequest) -> Result<DataResponse<M>, DataError> {
        let mut res = self.inner.load(req)?;
        let mut cast_result =
            res.payload.dynamic_cast_mut::<HelloWorldV1>();
        if let Ok(ref mut concrete_payload) = cast_result {
            // Add an emoji to the hello world message
            concrete_payload.with_mut(|data| {
                data.message.to_mut().insert_str(0, "");
            });
        }
        Ok(res)
    }
}

let provider = MyWrapper {
    inner: HelloWorldProvider,
};
let formatter =
    HelloWorldFormatter::try_new_unstable(&provider, locale!("de").into())
        .unwrap();

assert_eq!(formatter.format_to_string(), "✨ Hallo Welt");

impl<M> Clone for DataPayload<M>

fn clone(self: &Self) -> Self

impl<M> Debug for DataPayload<M>

fn fmt(self: &Self, f: &mut Formatter<'_>) -> Result

impl<M> Default for DataPayload<M>

fn default() -> Self

impl<M> Eq for DataPayload<M>

impl<M> Freeze for DataPayload<M>

impl<M> PartialEq for DataPayload<M>

fn eq(self: &Self, other: &Self) -> bool

impl<M> RefUnwindSafe for DataPayload<M>

impl<M> Send for DataPayload<M>

impl<M> Sync for DataPayload<M>

impl<M> Unpin for DataPayload<M>

impl<M> UnsafeUnpin for DataPayload<M>

impl<M> UnwindSafe for DataPayload<M>

impl<T> Any for DataPayload<M>

fn type_id(self: &Self) -> TypeId

impl<T> Borrow for DataPayload<M>

fn borrow(self: &Self) -> &T

impl<T> BorrowMut for DataPayload<M>

fn borrow_mut(self: &mut Self) -> &mut T

impl<T> CloneToUninit for DataPayload<M>

unsafe fn clone_to_uninit(self: &Self, dest: *mut u8)

impl<T> ErasedDestructor for DataPayload<M>

impl<T> From for DataPayload<M>

fn from(t: T) -> T

Returns the argument unchanged.

impl<T> ToOwned for DataPayload<M>

fn to_owned(self: &Self) -> T
fn clone_into(self: &Self, target: &mut T)

impl<T, U> Into for DataPayload<M>

fn into(self: Self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of [From]<T> for U chooses to do.

impl<T, U> TryFrom for DataPayload<M>

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

impl<T, U> TryInto for DataPayload<M>

fn try_into(self: Self) -> Result<U, <U as TryFrom<T>>::Error>