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 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§
Source§impl<M> DataPayload<M>where
M: DynamicDataMarker,
impl<M> DataPayload<M>where
M: DynamicDataMarker,
Sourcepub fn from_owned(data: M::DataStruct) -> Self
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 = HelloWorld {
message: Cow::Owned("example".to_owned()),
};
let payload = DataPayload::<HelloWorldV1>::from_owned(local_struct.clone());
assert_eq!(payload.get(), &local_struct);
Sourcepub const fn from_static_ref(data: &'static M::DataStruct) -> Self
pub const fn from_static_ref(data: &'static M::DataStruct) -> Self
Construct a DataPayload
from a static reference.
This is mainly used by databake.
Sourcepub 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>,
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::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);
Sourcepub fn get<'a>(&'a self) -> &'a <M::DataStruct as Yokeable<'a>>::Output
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::HelloWorldV1;
use icu_provider::prelude::*;
let payload = DataPayload::<HelloWorldV1>::from_static_str("Demo");
assert_eq!("Demo", payload.get().message);
Sourcepub fn get_static(
&self,
) -> Option<&'static <M::DataStruct as Yokeable<'static>>::Output>
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.
Sourcepub 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>,
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:
DataPayload::map_project_cloned()
if you don’t have ownership ofself
DataPayload::try_map_project()
to bubble up an errorDataPayload::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());
Sourcepub 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,
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<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());
Sourcepub 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>,
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<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());
Sourcepub 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>,
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<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());
Sourcepub fn cast<M2>(self) -> DataPayload<M2>where
M2: DynamicDataMarker<DataStruct = M::DataStruct>,
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 CustomHelloWorldV1;
impl DynamicDataMarker for CustomHelloWorldV1 {
type DataStruct = HelloWorld<'static>;
}
let hello_world: DataPayload<HelloWorldV1> = todo!();
let custom: DataPayload<CustomHelloWorldV1> = hello_world.cast();
Sourcepub fn cast_ref<M2>(&self) -> &DataPayload<M2>where
M2: DynamicDataMarker<DataStruct = M::DataStruct>,
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.
Sourcepub fn dynamic_cast<M2>(self) -> Result<DataPayload<M2>, DataError>where
M2: DynamicDataMarker,
pub fn dynamic_cast<M2>(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");
Sourcepub fn dynamic_cast_mut<M2>(
&mut self,
) -> Result<&mut DataPayload<M2>, DataError>where
M2: DynamicDataMarker,
pub fn dynamic_cast_mut<M2>(
&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");
Source§impl DataPayload<BufferMarker>
impl DataPayload<BufferMarker>
Sourcepub fn from_yoked_buffer(yoke: Yoke<&'static [u8], Option<Cart>>) -> Self
pub fn from_yoked_buffer(yoke: Yoke<&'static [u8], Option<Cart>>) -> Self
Converts a yoked byte buffer into a DataPayload<BufferMarker>
.
Sourcepub fn from_static_buffer(buffer: &'static [u8]) -> Self
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>
Cloning a DataPayload is generally a cheap operation.
See notes in the Clone
impl for Yoke
.
impl<M> Clone for DataPayload<M>
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<HelloWorldV1> = todo!();
let resp2 = resp1.clone();