Skip to content

Commit

Permalink
Add ExportableDataPayload::eq_dyn (#3639)
Browse files Browse the repository at this point in the history
  • Loading branch information
sffc authored Jul 6, 2023
1 parent 0ff9854 commit 0f57d72
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 11 deletions.
2 changes: 1 addition & 1 deletion components/calendar/src/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ impl FromStr for EraStartDate {
"datetime/week_data@1",
fallback_by = "region"
))]
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(
feature = "datagen",
derive(serde::Serialize, databake::Bake),
Expand Down
10 changes: 5 additions & 5 deletions components/list/src/provider/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const _: () = {
OrListV1Marker = "list/or@1",
UnitListV1Marker = "list/unit@1"
)]
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(
feature = "datagen",
derive(serde::Serialize, databake::Bake),
Expand Down Expand Up @@ -116,10 +116,10 @@ impl<'data> ListFormatterPatternsV1<'data> {
/// including in SemVer minor releases. While the serde representation of data structs is guaranteed
/// to be stable, their Rust representation might not be. Use with caution.
/// </div>
#[derive(Clone, Debug, yoke::Yokeable, zerofrom::ZeroFrom)]
#[derive(Clone, Debug, PartialEq, yoke::Yokeable, zerofrom::ZeroFrom)]
#[cfg_attr(
feature = "datagen",
derive(PartialEq, serde::Serialize, databake::Bake),
derive(serde::Serialize, databake::Bake),
databake(path = icu_list::provider),
)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
Expand All @@ -142,10 +142,10 @@ pub struct ConditionalListJoinerPattern<'data> {
/// including in SemVer minor releases. While the serde representation of data structs is guaranteed
/// to be stable, their Rust representation might not be. Use with caution.
/// </div>
#[derive(Clone, Debug, yoke::Yokeable, zerofrom::ZeroFrom)]
#[derive(Clone, Debug, PartialEq, yoke::Yokeable, zerofrom::ZeroFrom)]
#[cfg_attr(
feature = "datagen",
derive(PartialEq, serde::Serialize, databake::Bake),
derive(serde::Serialize, databake::Bake),
databake(path = icu_list::provider),
)]
pub struct SpecialCasePattern<'data> {
Expand Down
1 change: 0 additions & 1 deletion components/list/src/provider/serde_dfa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ pub struct SerdeDFA<'data> {
pattern: Option<Cow<'data, str>>,
}

#[cfg(feature = "datagen")]
impl PartialEq for SerdeDFA<'_> {
fn eq(&self, other: &Self) -> bool {
self.dfa_bytes == other.dfa_bytes
Expand Down
8 changes: 4 additions & 4 deletions components/properties/src/provider/names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ impl NormalizedPropertyNameStr {
/// including in SemVer minor releases. While the serde representation of data structs is guaranteed
/// to be stable, their Rust representation might not be. Use with caution.
/// </div>
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
#[icu_provider::data_struct(marker(
GeneralCategoryMaskNameToValueV1Marker,
"propnames/from/gcm@1",
Expand All @@ -213,7 +213,7 @@ pub struct PropertyValueNameToEnumMapV1<'data> {
/// including in SemVer minor releases. While the serde representation of data structs is guaranteed
/// to be stable, their Rust representation might not be. Use with caution.
/// </div>
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
#[icu_provider::data_struct]
#[cfg_attr(
feature = "datagen",
Expand All @@ -236,7 +236,7 @@ pub struct PropertyEnumToValueNameSparseMapV1<'data> {
/// including in SemVer minor releases. While the serde representation of data structs is guaranteed
/// to be stable, their Rust representation might not be. Use with caution.
/// </div>
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
#[icu_provider::data_struct]
#[cfg_attr(
feature = "datagen",
Expand All @@ -260,7 +260,7 @@ pub struct PropertyEnumToValueNameLinearMapV1<'data> {
/// including in SemVer minor releases. While the serde representation of data structs is guaranteed
/// to be stable, their Rust representation might not be. Use with caution.
/// </div>
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
#[icu_provider::data_struct]
#[cfg_attr(
feature = "datagen",
Expand Down
84 changes: 84 additions & 0 deletions provider/core/src/datagen/payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

use core::any::Any;

use crate::dynutil::UpcastDataPayload;
use crate::prelude::*;
use alloc::boxed::Box;
use databake::{Bake, CrateEnv, TokenStream};
use yoke::trait_hack::YokeTraitHack;
use yoke::*;

trait ExportableDataPayload {
Expand All @@ -14,11 +17,14 @@ trait ExportableDataPayload {
&self,
serializer: &mut dyn erased_serde::Serializer,
) -> Result<(), DataError>;
fn as_any(&self) -> &dyn Any;
fn eq_dyn(&self, other: &dyn ExportableDataPayload) -> bool;
}

impl<M: DataMarker> ExportableDataPayload for DataPayload<M>
where
for<'a> <M::Yokeable as Yokeable<'a>>::Output: Bake + serde::Serialize,
for<'a> YokeTraitHack<<M::Yokeable as Yokeable<'a>>::Output>: PartialEq,
{
fn bake_yoke(&self, ctx: &CrateEnv) -> TokenStream {
self.get().bake(ctx)
Expand All @@ -34,6 +40,25 @@ where
.map_err(|e| DataError::custom("Serde export").with_display_context(&e))?;
Ok(())
}

fn as_any(&self) -> &dyn Any {
self
}

fn eq_dyn(&self, other: &dyn ExportableDataPayload) -> bool {
match other.as_any().downcast_ref::<Self>() {
Some(downcasted) => (*self).eq(downcasted),
None => {
debug_assert!(
false,
"cannot compare ExportableDataPayloads of different types: self is {:?} but other is {:?}",
self.type_id(),
other.as_any().type_id(),
);
false
}
}
}
}

#[doc(hidden)] // exposed for make_exportable_provider
Expand All @@ -42,6 +67,12 @@ pub struct ExportBox {
payload: Box<dyn ExportableDataPayload + Sync + Send>,
}

impl PartialEq for ExportBox {
fn eq(&self, other: &Self) -> bool {
self.payload.eq_dyn(&*other.payload)
}
}

impl core::fmt::Debug for ExportBox {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ExportBox")
Expand All @@ -55,6 +86,7 @@ where
M: DataMarker,
M::Yokeable: Sync + Send,
for<'a> <M::Yokeable as Yokeable<'a>>::Output: Bake + serde::Serialize,
for<'a> YokeTraitHack<<M::Yokeable as Yokeable<'a>>::Output>: PartialEq,
{
fn upcast(other: DataPayload<M>) -> DataPayload<ExportMarker> {
DataPayload::from_owned(ExportBox {
Expand Down Expand Up @@ -143,3 +175,55 @@ pub struct ExportMarker {}
impl DataMarker for ExportMarker {
type Yokeable = ExportBox;
}

#[cfg(test)]
mod tests {
use super::*;
use crate::hello_world::*;

#[test]
fn test_compare_with_dyn() {
let payload1: DataPayload<HelloWorldV1Marker> = DataPayload::from_owned(HelloWorldV1 {
message: "abc".into(),
});
let payload2: DataPayload<HelloWorldV1Marker> = DataPayload::from_owned(HelloWorldV1 {
message: "abc".into(),
});
let payload3: DataPayload<HelloWorldV1Marker> = DataPayload::from_owned(HelloWorldV1 {
message: "def".into(),
});

assert!(payload1.eq_dyn(&payload2));
assert!(payload2.eq_dyn(&payload1));

assert!(!payload1.eq_dyn(&payload3));
assert!(!payload3.eq_dyn(&payload1));
}

#[test]
fn test_export_marker_partial_eq() {
let payload1: DataPayload<ExportMarker> =
UpcastDataPayload::upcast(DataPayload::<HelloWorldV1Marker>::from_owned(
HelloWorldV1 {
message: "abc".into(),
},
));
let payload2: DataPayload<ExportMarker> =
UpcastDataPayload::upcast(DataPayload::<HelloWorldV1Marker>::from_owned(
HelloWorldV1 {
message: "abc".into(),
},
));
let payload3: DataPayload<ExportMarker> =
UpcastDataPayload::upcast(DataPayload::<HelloWorldV1Marker>::from_owned(
HelloWorldV1 {
message: "def".into(),
},
));

assert_eq!(payload1, payload2);
assert_eq!(payload2, payload1);
assert_ne!(payload1, payload3);
assert_ne!(payload3, payload1);
}
}

0 comments on commit 0f57d72

Please sign in to comment.