Struct icu_provider::DataPayload

source ·
pub struct DataPayload<M: DynamicDataMarker>(/* private fields */);
Expand description

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 HelloWorldV1Marker marker:

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

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

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

Implementations§

source§

impl<M> DataPayload<M>

source

pub fn wrap_into_any_payload(self) -> AnyPayload

Converts this DataPayload into a type-erased AnyPayload. Unless the payload stores a static reference, this will move it to the heap.

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

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

let any_payload = payload.wrap_into_any_payload();

let payload: DataPayload<HelloWorldV1Marker> =
    any_payload.downcast().expect("TypeId matches");
assert_eq!("Custom Hello World", payload.get().message);
source§

impl DataPayload<AnyMarker>

source

pub fn downcast<M>(self) -> Result<DataPayload<M>, DataError>
where M: DynamicDataMarker, for<'a> YokeTraitHack<<M::DataStruct as Yokeable<'a>>::Output>: Clone, M::DataStruct: ZeroFrom<'static, M::DataStruct> + MaybeSendSync,

Transforms a type-erased DataPayload<AnyMarker> into a concrete DataPayload<M>.

source§

impl DataPayload<BufferMarker>

source

pub fn into_deserialized<M>( self, buffer_format: BufferFormat, ) -> Result<DataPayload<M>, DataError>
where M: DynamicDataMarker, for<'de> YokeTraitHack<<M::DataStruct as Yokeable<'de>>::Output>: Deserialize<'de>,

Deserialize a DataPayload<BufferMarker> into a DataPayload of a specific concrete type.

This requires enabling the deserialization Cargo feature for the expected format(s):

  • deserialize_json
  • deserialize_postcard_1
  • deserialize_bincode_1

This function takes the buffer format as an argument. When a buffer payload is returned from a data provider, the buffer format is stored in the DataResponseMetadata.

§Examples

Requires the deserialize_json Cargo feature:

use icu_provider::buf::BufferFormat;
use icu_provider::hello_world::*;
use icu_provider::prelude::*;

let buffer: &[u8] = br#"{"message":"Hallo Welt"}"#;

let buffer_payload = DataPayload::from_owned(buffer);
let payload: DataPayload<HelloWorldV1Marker> = buffer_payload
    .into_deserialized(BufferFormat::Json)
    .expect("Deserialization successful");

assert_eq!(payload.get().message, "Hallo Welt");
source§

impl DataPayload<ExportMarker>

source

pub fn serialize<S>(&self, serializer: S) -> Result<(), DataError>
where S: Serializer, S::Ok: 'static,

Serializes this DataPayload into a serializer using Serde.

§Examples
use icu_provider::export::*;
use icu_provider::dynutil::UpcastDataPayload;
use icu_provider::hello_world::HelloWorldV1Marker;
use icu_provider::prelude::*;

// Create an example DataPayload
let payload: DataPayload<HelloWorldV1Marker> = Default::default();
let export: DataPayload<ExportMarker> = UpcastDataPayload::upcast(payload);

// Serialize the payload to a JSON string
let mut buffer: Vec<u8> = vec![];
export
    .serialize(&mut serde_json::Serializer::new(&mut buffer))
    .expect("Serialization should succeed");
assert_eq!(r#"{"message":"(und) Hello World"}"#.as_bytes(), buffer);
source

pub fn tokenize(&self, env: &CrateEnv) -> TokenStream

Serializes this DataPayload’s value into a TokenStream using its [Bake] implementations.

§Examples
use icu_provider::export::*;
use icu_provider::dynutil::UpcastDataPayload;
use icu_provider::hello_world::HelloWorldV1Marker;
use icu_provider::prelude::*;

// Create an example DataPayload
let payload: DataPayload<HelloWorldV1Marker> = Default::default();
let export: DataPayload<ExportMarker> = UpcastDataPayload::upcast(payload);

let env = databake::CrateEnv::default();
let tokens = export.tokenize(&env);
assert_eq!(
    quote! {
        icu_provider::hello_world::HelloWorldV1 {
            message: alloc::borrow::Cow::Borrowed("(und) Hello World"),
        }
    }
    .to_string(),
    tokens.to_string()
);
assert_eq!(
    env.into_iter().collect::<BTreeSet<_>>(),
    ["icu_provider", "alloc"]
        .into_iter()
        .collect::<BTreeSet<_>>()
);
source

pub fn postcard_size(&self) -> usize

Returns the data size using postcard encoding

source

pub fn baked_size(&self) -> usize

Returns an estimate of the baked size, made up of the size of the struct itself, as well as the sizes of all its static borrows.

As static borrows are deduplicated by the linker, this is often overcounting.

source§

impl DataPayload<ExportMarker>

source

pub fn hash_and_postcard_size<H: Hasher>(&self, state: &mut H) -> usize

Calculates a payload hash and the postcard size

source§

impl DataPayload<HelloWorldV1Marker>

source

pub fn from_static_str(s: &'static str) -> DataPayload<HelloWorldV1Marker>

Make a DataPayload<HelloWorldV1Marker> from a static string slice.

source§

impl<M> DataPayload<M>

source

pub fn from_owned(data: M::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 = HelloWorldV1 {
    message: Cow::Owned("example".to_owned()),
};

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

assert_eq!(payload.get(), &local_struct);
source

pub const fn from_static_ref(data: &'static M::DataStruct) -> Self

Construct a DataPayload from a static reference.

This is mainly used by databake.

source

pub fn with_mut<'a, F>(&'a mut self, f: F)
where F: 'static + for<'b> FnOnce(&'b mut <M::DataStruct as Yokeable<'a>>::Output), M::DataStruct: ZeroFrom<'static, M::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::HelloWorldV1Marker;
use icu_provider::prelude::*;

let mut payload =
    DataPayload::<HelloWorldV1Marker>::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::HelloWorldV1Marker;
use icu_provider::prelude::*;

let mut payload =
    DataPayload::<HelloWorldV1Marker>::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);
source

pub fn get<'a>(&'a self) -> &'a <M::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::HelloWorldV1Marker;
use icu_provider::prelude::*;

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

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

pub fn get_static( &self, ) -> Option<&'static <M::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.

source

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

§Examples

Map from HelloWorldV1 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<HelloWorldV1Marker> = DataPayload::from_owned(HelloWorldV1 {
    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());
source

pub fn map_project_cloned<'this, M2, F>(&'this self, f: F) -> DataPayload<M2>
where M2: DynamicDataMarker, F: for<'a> FnOnce(&'this <M::DataStruct as Yokeable<'a>>::Output, PhantomData<&'a ()>) -> <M2::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

let p1: DataPayload<HelloWorldV1Marker> =
    DataPayload::from_owned(HelloWorldV1 {
        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());
source

pub fn try_map_project<M2, F, E>(self, f: F) -> Result<DataPayload<M2>, E>
where M2: DynamicDataMarker, F: for<'a> FnOnce(<M::DataStruct as Yokeable<'a>>::Output, PhantomData<&'a ()>) -> Result<<M2::DataStruct as Yokeable<'a>>::Output, E>, M::DataStruct: ZeroFrom<'static, M::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

let p1: DataPayload<HelloWorldV1Marker> =
    DataPayload::from_owned(HelloWorldV1 {
        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());
source

pub fn try_map_project_cloned<'this, M2, F, E>( &'this self, f: F, ) -> Result<DataPayload<M2>, E>
where M2: DynamicDataMarker, F: for<'a> FnOnce(&'this <M::DataStruct as Yokeable<'a>>::Output, PhantomData<&'a ()>) -> Result<<M2::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

let p1: DataPayload<HelloWorldV1Marker> =
    DataPayload::from_owned(HelloWorldV1 {
        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());
source

pub fn cast<M2>(self) -> DataPayload<M2>
where M2: DynamicDataMarker<DataStruct = M::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 CustomHelloWorldV1Marker;
impl DynamicDataMarker for CustomHelloWorldV1Marker {
    type DataStruct = HelloWorldV1<'static>;
}

let hello_world: DataPayload<HelloWorldV1Marker> = todo!();
let custom: DataPayload<CustomHelloWorldV1Marker> = hello_world.cast();
source

pub fn cast_ref<M2>(&self) -> &DataPayload<M2>
where M2: DynamicDataMarker<DataStruct = M::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.

source

pub fn dynamic_cast<M2>(self) -> Result<DataPayload<M2>, DataError>

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 std::any::TypeId;
use std::borrow::Cow;
use icu_locale_core::locale;
use icu_provider::hello_world::*;
use icu_provider::prelude::*;
use icu_provider_adapters::empty::EmptyDataProvider;

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<HelloWorldV1Marker>,
{
    #[inline]
    fn load(&self, req: DataRequest) -> Result<DataResponse<M>, DataError> {
        if TypeId::of::<HelloWorldV1Marker>() == TypeId::of::<M>() {
            let response = DataProvider::<HelloWorldV1Marker>::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");
source

pub fn dynamic_cast_mut<M2>( &mut self, ) -> Result<&mut DataPayload<M2>, DataError>

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::<HelloWorldV1Marker>();
        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");
source§

impl DataPayload<BufferMarker>

source

pub fn from_owned_buffer(buffer: Box<[u8]>) -> Self

Converts an owned byte buffer into a DataPayload<BufferMarker>.

source

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

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

source

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

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

Trait Implementations§

source§

impl<M> Clone for DataPayload<M>
where M: DynamicDataMarker, for<'a> YokeTraitHack<<M::DataStruct as Yokeable<'a>>::Output>: Clone,

Cloning a DataPayload is generally a cheap operation. See notes in the Clone impl for [Yoke].

§Examples

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

let resp1: DataPayload<HelloWorldV1Marker> = todo!();
let resp2 = resp1.clone();
source§

fn clone(&self) -> Self

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<M> Debug for DataPayload<M>
where M: DynamicDataMarker, for<'a> &'a <M::DataStruct as Yokeable<'a>>::Output: Debug,

source§

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

Formats the value using the given formatter. Read more
source§

impl<M> Default for DataPayload<M>

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl Hash for DataPayload<ExportMarker>

source§

fn hash<H: Hasher>(&self, state: &mut H)

Feeds this value into the given Hasher. Read more
1.3.0 · source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
source§

impl<M> PartialEq for DataPayload<M>
where M: DynamicDataMarker, for<'a> YokeTraitHack<<M::DataStruct as Yokeable<'a>>::Output>: PartialEq,

source§

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

Tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
source§

impl<M> Eq for DataPayload<M>
where M: DynamicDataMarker, for<'a> YokeTraitHack<<M::DataStruct as Yokeable<'a>>::Output>: Eq,

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

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

Mutably borrows from an owned value. Read more
source§

impl<T> CloneToUninit for T
where T: Clone,

source§

unsafe fn clone_to_uninit(&self, dst: *mut T)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dst. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

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

source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
source§

impl<T> ToOwned for T
where T: Clone,

source§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

source§

type Error = Infallible

The type returned in the event of a conversion error.
source§

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

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

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

Performs the conversion.
§

impl<T> ErasedDestructor for T
where T: 'static,

source§

impl<T> MaybeSendSync for T
where T: Send + Sync,