Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ExportableDataPayload::eq_dyn #3639

Merged
merged 6 commits into from
Jul 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 {
sffc marked this conversation as resolved.
Show resolved Hide resolved
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);
}
}