Skip to content

Commit

Permalink
Add DataPayload::dynamic_cast with example (#5467)
Browse files Browse the repository at this point in the history
  • Loading branch information
sffc committed Aug 30, 2024
1 parent c3e6abc commit 6b5a69c
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions provider/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ databake = { workspace = true, optional = true, features = ["derive"] }
serde_json = { workspace = true }
icu = { path = "../../components/icu", default-features = false }
icu_locale = { path = "../../components/locale" }
icu_provider_adapters = { path = "../adapters", default-features = false }

[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
criterion = { workspace = true }
Expand Down
75 changes: 75 additions & 0 deletions provider/core/src/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,81 @@ where
})
}

/// 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");
/// ```
pub fn dynamic_cast<M2>(self) -> Result<DataPayload<M2>, DataError>
where
M2: DynamicDataMarker,
{
let mut option_self = Some(self);
let mut option_out = None::<DataPayload<M2>>;
if let Some(x) = (&mut option_out as &mut dyn core::any::Any).downcast_mut() {
core::mem::swap(&mut option_self, x);
debug_assert!(option_out.is_some());
if let Some(out) = option_out {
return Ok(out);
}
}
Err(DataError::for_type::<M2>().with_str_context(core::any::type_name::<M>()))
}

/// Convert a mutable reference of a [`DataPayload`] to another mutable reference
/// of the same type with runtime type checking.
///
Expand Down
4 changes: 4 additions & 0 deletions tutorials/data_provider.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,10 @@ let formatter = FixedDecimalFormatter::try_new_unstable(
assert_eq!(formatter.format_to_string(&100007i64.into()), "100🐮007");
```

## Forking Data Providers

Forking providers can be implemented using `DataPayload::dynamic_cast`. For an example, see that function's documentation.

## Accessing the Resolved Locale

ICU4X objects do not store their "resolved locale" because that is not a well-defined concept. Components can load data from many sources, and fallbacks to parent locales or root does not necessarily mean that a locale is not supported.
Expand Down

0 comments on commit 6b5a69c

Please sign in to comment.