Skip to content

Commit

Permalink
data_marker! macro (#6072)
Browse files Browse the repository at this point in the history
This can replace the `#[data_struct]` proc macro.

#5124
  • Loading branch information
robertbastian authored Feb 5, 2025
1 parent 4d2e35d commit eaaf49f
Show file tree
Hide file tree
Showing 8 changed files with 79 additions and 107 deletions.
24 changes: 9 additions & 15 deletions components/properties/src/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -479,21 +479,15 @@ pub enum PropertyCodePointMap<'data, T: TrieValue> {
macro_rules! data_struct_generic {
($(marker($marker:ident, $ty:ident, $path:literal),)+) => {
$(
#[doc = core::concat!("Data marker for the '", stringify!($ty), "' Unicode property")]
#[derive(Debug, Default)]
#[cfg_attr(feature = "datagen", derive(databake::Bake))]
#[cfg_attr(feature = "datagen", databake(path = icu_properties::provider))]
pub struct $marker;
impl icu_provider::DynamicDataMarker for $marker {
type DataStruct = PropertyCodePointMap<'static, $ty>;
}
impl icu_provider::DataMarker for $marker {
const INFO: icu_provider::DataMarkerInfo = {
let mut info = DataMarkerInfo::from_id(icu_provider::marker::data_marker_id!($marker));
info.is_singleton = true;
info
};
}
data_marker!(
#[doc = core::concat!("Data marker for the '", stringify!($ty), "' Unicode property")]
#[derive(Debug, Default)]
#[cfg_attr(feature = "datagen", derive(databake::Bake))]
#[cfg_attr(feature = "datagen", databake(path = icu_properties::provider))]
$marker,
PropertyCodePointMap<'static, $ty>,
is_singleton = true,
);
)+
}
}
Expand Down
10 changes: 2 additions & 8 deletions provider/adapters/src/fixed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,10 @@ use yoke::Yokeable;
/// .expect("marker matches");
/// assert_writeable_eq!(formatter.format(), "custom hello world");
///
/// # struct DummyMarkerV1;
/// # impl DynamicDataMarker for DummyMarkerV1 {
/// # type DataStruct = <HelloWorldV1 as DynamicDataMarker>::DataStruct;
/// # }
/// # impl DataMarker for DummyMarkerV1 {
/// # const INFO: DataMarkerInfo = DataMarkerInfo::from_id(icu_provider::marker::data_marker_id!(DummyMarkerV1));
/// # }
/// data_marker!(DummyV1, <HelloWorldV1 as DynamicDataMarker>::DataStruct);
/// // Requests for invalid markers get MissingDataMarker
/// assert!(matches!(
/// provider.load_any(DummyMarkerV1::INFO, Default::default()),
/// provider.load_any(DummyV1::INFO, Default::default()),
/// Err(DataError {
/// kind: DataErrorKind::MarkerNotFound,
/// ..
Expand Down
14 changes: 2 additions & 12 deletions provider/core/src/data_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -458,22 +458,12 @@ mod test {
#[derive(
Serialize, Deserialize, Debug, Clone, Default, PartialEq, yoke::Yokeable, zerofrom::ZeroFrom,
)]
struct HelloAlt {
pub struct HelloAlt {
#[zerofrom(clone)]
message: String,
}

/// Marker type for [`HelloAlt`].
struct HelloAltMarkerV1 {}

impl DynamicDataMarker for HelloAltMarkerV1 {
type DataStruct = HelloAlt;
}

impl DataMarker for HelloAltMarkerV1 {
const INFO: DataMarkerInfo =
DataMarkerInfo::from_id(crate::marker::data_marker_id!(HelloAltMarkerV1));
}
data_marker!(HelloAltMarkerV1, HelloAlt);

#[derive(Deserialize, Debug, Clone, Default, PartialEq)]
struct HelloCombined<'data> {
Expand Down
21 changes: 5 additions & 16 deletions provider/core/src/dynutil.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,16 +118,11 @@ pub use __impl_casting_upcast as impl_casting_upcast;
/// ..Default::default()
/// }).unwrap();
///
/// # struct DummyMarkerV1;
/// # impl DynamicDataMarker for DummyMarkerV1 {
/// # type DataStruct = <HelloWorldV1 as DynamicDataMarker>::DataStruct;
/// # }
/// # impl DataMarker for DummyMarkerV1 {
/// # const INFO: DataMarkerInfo = DataMarkerInfo::from_id(icu_provider::marker::data_marker_id!(DummyMarkerV1));
/// # }
/// data_marker!(DummyV1, <HelloWorldV1 as DynamicDataMarker>::DataStruct);
///
/// // MissingDataMarker error as the marker does not match:
/// assert_eq!(
/// HelloWorldProvider.load_data(DummyMarkerV1::INFO, DataRequest {
/// HelloWorldProvider.load_data(DummyV1::INFO, DataRequest {
/// id: DataIdentifierBorrowed::for_locale(&langid!("de").into()),
/// ..Default::default()
/// }).unwrap_err().kind,
Expand Down Expand Up @@ -169,14 +164,8 @@ pub use __impl_casting_upcast as impl_casting_upcast;
/// }).unwrap();
///
/// // Because of the wildcard, any marker actually works:
/// struct DummyMarkerV1;
/// impl DynamicDataMarker for DummyMarkerV1 {
/// type DataStruct = <HelloWorldV1 as DynamicDataMarker>::DataStruct;
/// }
/// impl DataMarker for DummyMarkerV1 {
/// const INFO: DataMarkerInfo = DataMarkerInfo::from_id(icu_provider::marker::data_marker_id!(DummyMarkerV1));
/// }
/// HelloWorldProvider.as_any_provider().load_any(DummyMarkerV1::INFO, DataRequest {
/// data_marker!(DummyV1, <HelloWorldV1 as DynamicDataMarker>::DataStruct);
/// HelloWorldProvider.as_any_provider().load_any(DummyV1::INFO, DataRequest {
/// id: DataIdentifierBorrowed::for_locale(&langid!("de").into()),
/// ..Default::default()
/// }).unwrap();
Expand Down
21 changes: 7 additions & 14 deletions provider/core/src/hello_world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,20 +41,13 @@ impl Default for HelloWorld<'_> {
}
}

/// Marker type for [`HelloWorld`].
#[derive(Debug)]
pub struct HelloWorldV1;

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

impl DataMarker for HelloWorldV1 {
const INFO: icu_provider::DataMarkerInfo = DataMarkerInfo {
has_checksum: true,
..DataMarkerInfo::from_id(icu_provider::marker::data_marker_id!(HelloWorldV1))
};
}
data_marker!(
/// Marker type for [`HelloWorld`].
#[derive(Debug)]
HelloWorldV1,
HelloWorld<'static>,
has_checksum = true,
);

/// A data provider returning Hello World strings in different languages.
///
Expand Down
8 changes: 4 additions & 4 deletions provider/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ pub use response::{Cart, DataPayload, DataResponse, DataResponseMetadata};
#[path = "marker.rs"]
mod marker_full;

pub use marker_full::{DataMarker, DataMarkerInfo, DynamicDataMarker};
pub use marker_full::{data_marker, DataMarker, DataMarkerInfo, DynamicDataMarker};
pub mod marker {
//! Additional [`DataMarker`](super::DataMarker) helpers.
Expand All @@ -170,9 +170,9 @@ pub mod prelude {
pub use crate::request::*;
#[doc(no_inline)]
pub use crate::{
marker::DataMarkerExt, BoundDataProvider, DataError, DataErrorKind, DataLocale, DataMarker,
DataMarkerAttributes, DataMarkerInfo, DataPayload, DataProvider, DataRequest,
DataRequestMetadata, DataResponse, DataResponseMetadata, DryDataProvider,
data_marker, marker::DataMarkerExt, BoundDataProvider, DataError, DataErrorKind,
DataLocale, DataMarker, DataMarkerAttributes, DataMarkerInfo, DataPayload, DataProvider,
DataRequest, DataRequestMetadata, DataResponse, DataResponseMetadata, DryDataProvider,
DynamicDataMarker, DynamicDataProvider, DynamicDryDataProvider, IterableDataProvider,
IterableDynamicDataProvider, ResultDataError,
};
Expand Down
79 changes: 49 additions & 30 deletions provider/core/src/marker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -328,22 +328,10 @@ impl AsULE for DataMarkerIdHash {
// Safe since the ULE type is `self`.
unsafe impl EqULE for DataMarkerIdHash {}

/// The id of a data marker.
/// The ID of a data marker.
///
/// ```
/// # use icu_provider::marker::DataMarkerId;
/// const K: DataMarkerId =
/// icu_provider::marker::data_marker_id!(DataV1);
/// ```
///
/// The identifier ends with `V` followed by one or more digits (the version number).
///
/// Invalid identifiers are compile-time errors (as [`data_marker_id!`](crate::marker::data_marker_id) uses `const`).
///
/// ```compile_fail,E0080
/// # use icu_provider::marker::DataMarkerId;
/// const K: DataMarkerId = icu_provider::marker::data_marker_id!(Data);
/// ```
/// This is generally a [`DataMarkerIdHash`]. If debug assertions or the `export` Cargo feature
/// are enabled, this also contains a human-readable string for an improved `Debug` implementation.
#[derive(Debug, Copy, Clone, Eq)]
pub struct DataMarkerId {
/// The human-readable path string ends with `@` followed by one or more digits (the version
Expand Down Expand Up @@ -424,14 +412,11 @@ impl DataMarkerId {
/// # Example
///
/// ```
/// use icu_provider::marker::DataMarkerId;
/// use icu_provider::marker::DataMarkerIdHash;
/// use icu_provider::prelude::*;
///
/// const ID: DataMarkerId =
/// icu_provider::marker::data_marker_id!(FooMarkerV1);
/// const ID_HASH: DataMarkerIdHash = ID.hashed();
/// icu_provider::data_marker!(FooV1, &'static str);
///
/// assert_eq!(ID_HASH.to_bytes(), [66, 225, 223, 59]);
/// assert_eq!(FooV1::INFO.id.hashed().to_bytes(), [198, 217, 86, 48]);
/// ```
#[inline]
pub const fn hashed(self) -> DataMarkerIdHash {
Expand Down Expand Up @@ -513,25 +498,20 @@ impl DataMarkerInfo {
/// ```
/// use icu_provider::prelude::*;
/// use icu_provider::hello_world::*;
/// # struct DummyMarkerV1;
/// # impl DynamicDataMarker for DummyMarkerV1 {
/// # type DataStruct = <HelloWorldV1 as DynamicDataMarker>::DataStruct;
/// # }
/// # impl DataMarker for DummyMarkerV1 {
/// # const INFO: DataMarkerInfo = DataMarkerInfo::from_id(icu_provider::marker::data_marker_id!(DummyMarkerV1));
/// # }
///
/// icu_provider::data_marker!(DummyV1, <HelloWorldV1 as DynamicDataMarker>::DataStruct);
///
/// assert!(matches!(HelloWorldV1::INFO.match_marker(HelloWorldV1::INFO), Ok(())));
/// assert!(matches!(
/// HelloWorldV1::INFO.match_marker(DummyMarkerV1::INFO),
/// HelloWorldV1::INFO.match_marker(DummyV1::INFO),
/// Err(DataError {
/// kind: DataErrorKind::MarkerNotFound,
/// ..
/// })
/// ));
///
/// // The error context contains the argument:
/// assert_eq!(HelloWorldV1::INFO.match_marker(DummyMarkerV1::INFO).unwrap_err().marker, Some(DummyMarkerV1::INFO.id));
/// assert_eq!(HelloWorldV1::INFO.match_marker(DummyV1::INFO).unwrap_err().marker, Some(DummyV1::INFO.id));
/// ```
pub fn match_marker(self, marker: Self) -> Result<(), DataError> {
if self == marker {
Expand Down Expand Up @@ -569,6 +549,45 @@ macro_rules! __data_marker_id {
#[doc(inline)]
pub use __data_marker_id as data_marker_id;

/// Creates a data marker.
///
/// # Examples
///
/// ```
/// icu_provider::data_marker!(DummyV1, &'static str);
/// ```
///
/// The identifier needs to end with a `V` followed by one or more digits (the version number).
///
/// Invalid identifiers are compile-time errors (as [`data_marker!`](crate::marker::data_marker) uses `const`).
///
/// ```compile_fail,E0080
/// icu_provider::data_marker!(Dummy, &'static str);
/// ```
#[macro_export]
#[doc(hidden)] // macro
macro_rules! __data_marker {
($(#[$doc:meta])* $name:ident, $struct:ty $(, $info_field:ident = $info_val:expr)* $(,)?) => {
$(#[$doc])*
pub struct $name;
impl $crate::DynamicDataMarker for $name {
type DataStruct = $struct;
}
impl $crate::DataMarker for $name {
const INFO: $crate::DataMarkerInfo = {
#[allow(unused_mut)]
let mut info = $crate::DataMarkerInfo::from_id($crate::marker::data_marker_id!($name));
$(
info.$info_field = $info_val;
)*
info
};
}
}
}
#[doc(inline)]
pub use __data_marker as data_marker;

impl fmt::Debug for DataMarkerInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
#[cfg(any(feature = "export", debug_assertions))]
Expand Down
9 changes: 1 addition & 8 deletions provider/fs/tests/test_file_io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,7 @@ fn test_errors() {
"{err:?}"
);

struct WrongV1;
impl DynamicDataMarker for WrongV1 {
type DataStruct = HelloWorld<'static>;
}
impl DataMarker for WrongV1 {
const INFO: DataMarkerInfo =
DataMarkerInfo::from_id(icu_provider::marker::data_marker_id!(WrongV1));
}
icu_provider::data_marker!(WrongV1, HelloWorld<'static>);

let err: Result<DataResponse<WrongV1>, DataError> =
provider.as_deserializing().load(Default::default());
Expand Down

0 comments on commit eaaf49f

Please sign in to comment.