Skip to content

Commit

Permalink
[refactor] #1981, #4195, #3068: Use a custom JSON serialization for e…
Browse files Browse the repository at this point in the history
…vent sets

Signed-off-by: Nikita Strygin <[email protected]>
  • Loading branch information
DCNick3 committed Mar 13, 2024
1 parent 63c709f commit d782ec5
Show file tree
Hide file tree
Showing 5 changed files with 222 additions and 20 deletions.
137 changes: 120 additions & 17 deletions data_model/derive/src/event_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,16 +132,15 @@ impl ToTokens for EventSetEnum {
} = self;

// definitions of consts for each event
let flag_defs = variants.iter().enumerate().map(
let flag_defs = variants.iter().zip(0u32..).map(
|(
i,
EventSetVariant {
flag_ident,
event_ident,
..
},
i,
)| {
let i = u32::try_from(i).unwrap();
let doc = format!(" Matches [`{event_enum_ident}::{event_ident}`]");
quote! {
#[doc = #doc]
Expand All @@ -152,23 +151,24 @@ impl ToTokens for EventSetEnum {
// identifiers of all the flag constants to use in impls
let flag_idents = variants
.iter()
.map(
|EventSetVariant {
flag_ident: ident, ..
}| quote!(Self::#ident),
)
.map(|EventSetVariant { flag_ident, .. }| quote!(#set_ident::#flag_ident))
.collect::<Vec<_>>();
// names of all the flag (as string literals) to use in debug impl
let flag_names = variants.iter().map(
|EventSetVariant {
flag_ident: ident, ..
}| {
let flag_name = ident.to_string();
let flag_names = variants
.iter()
.map(|EventSetVariant { flag_ident, .. }| {
let flag_name = flag_ident.to_string();
quote! {
#flag_name
}
},
);
})
.collect::<Vec<_>>();
// names of all the flags in `quotes` to use in error message
let flag_names_str = variants
.iter()
.map(|EventSetVariant { flag_ident, .. }| format!("`{flag_ident}`"))
.collect::<Vec<_>>()
.join(", ");
// patterns for matching events in the `matches` method
let event_patterns = variants.iter().map(
|EventSetVariant {
Expand Down Expand Up @@ -197,8 +197,6 @@ impl ToTokens for EventSetEnum {
// but it's the easiest way to make sure those traits are implemented
parity_scale_codec::Decode,
parity_scale_codec::Encode,
serde::Deserialize,
serde::Serialize,
// TODO: we probably want to represent the bit values for each variant in the schema
iroha_schema::IntoSchema,
)]
Expand Down Expand Up @@ -241,6 +239,19 @@ impl ToTokens for EventSetEnum {
(self.0 & other.0) == other.0
}

/// Decomposes an `EventSet` into a vector of basis `EventSet`s, each containing a single event
///
/// Each of the event set in the vector will be equal to some of the associated constants for the `EventSet`
pub fn decompose(&self) -> Vec<Self> {
let mut result = Vec::new();

#(if self.contains(#flag_idents) {
result.push(#flag_idents);
})*

result
}

/// Checks whether an event set contains a specific event
pub const fn matches(&self, event: &#event_enum_ident) -> bool {
match event {
Expand Down Expand Up @@ -277,6 +288,98 @@ impl ToTokens for EventSetEnum {
self.or(rhs)
}
}

impl core::ops::BitOrAssign for #set_ident {
fn bitor_assign(&mut self, rhs: Self) {
*self = self.or(rhs);
}
}

impl serde::Serialize for #set_ident {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::*;

let basis = self.decompose();
let mut seq = serializer.serialize_seq(Some(basis.len()))?;
for event in basis {
let str = match event {
#(#flag_idents => #flag_names,)*
_ => unreachable!(),
};

seq.serialize_element(&str)?;
}
seq.end()
}
}

impl<'de> serde::Deserialize<'de> for #set_ident {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct Visitor;

impl<'de> serde::de::Visitor<'de> for Visitor {
type Value = #set_ident;

fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
formatter.write_str("a sequence of strings")
}

fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: serde::de::SeqAccess<'de>,
{
let mut result = #set_ident::empty();

struct SingleEvent(#set_ident);
impl<'de> serde::Deserialize<'de> for SingleEvent {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct Visitor;

impl<'de> serde::de::Visitor<'de> for Visitor {
type Value = SingleEvent;

fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
formatter.write_str("a string")
}

fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
let event = match v {
#(
#flag_names => #flag_idents,
)*
_ => return Err(serde::de::Error::custom(format!("unknown event variant `{}`, expected one of {}", v, #flag_names_str))),
};

Ok(SingleEvent(event))
}
}

deserializer.deserialize_str(Visitor)
}
}
while let Some(SingleEvent(event)) = seq.next_element::<SingleEvent>()? {
result |= event;
}

Ok(result)
}
}

deserializer.deserialize_seq(Visitor)
}
}
})
}
}
Expand Down
4 changes: 2 additions & 2 deletions data_model/derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,8 @@ pub fn has_origin_derive(input: TokenStream) -> TokenStream {
/// const fn or(self, other: TestEventSet) -> TestEventSet;
/// /// Checks if the `other` set is a subset of `self` set
/// const fn contains(&self, other: Self) -> bool;
/// /// Decomposes an `EventSet` into a vector of basis `EventSet`s, each containing a single event
/// fn decompose(&self) -> Vec<TestEvent>
/// /// Checks if the set contains a specific event
/// const fn matches(&self, event: &TestEvent) -> bool;
/// }
Expand All @@ -536,8 +538,6 @@ pub fn has_origin_derive(input: TokenStream) -> TokenStream {
/// Hash,
/// parity_scale_codec::Decode,
/// parity_scale_codec::Encode,
/// serde::Deserialize
/// serde::Serialize,
/// iroha_schema::IntoSchema,
/// )]
///
Expand Down
99 changes: 99 additions & 0 deletions data_model/derive/tests/event_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,89 @@ mod events {
}

use events::{AnotherEvent, TestEvent, TestEventSet};
use serde_json::json;

#[test]
fn serialize() {
assert_eq!(
serde_json::to_value(TestEventSet::Event1).unwrap(),
json!(["Event1"])
);
assert_eq!(
serde_json::to_value(TestEventSet::Event1 | TestEventSet::Event2).unwrap(),
json!(["Event1", "Event2"])
);
assert_eq!(
serde_json::to_value(TestEventSet::Event1 | TestEventSet::AnyNestedEvent).unwrap(),
json!(["Event1", "AnyNestedEvent"])
);
assert_eq!(
serde_json::to_value(TestEventSet::all()).unwrap(),
json!(["Event1", "Event2", "AnyNestedEvent"])
);
}

#[test]
fn deserialize() {
assert_eq!(
serde_json::from_value::<TestEventSet>(json!([])).unwrap(),
TestEventSet::empty()
);
assert_eq!(
serde_json::from_value::<TestEventSet>(json!(["Event1"])).unwrap(),
TestEventSet::Event1
);
assert_eq!(
serde_json::from_value::<TestEventSet>(json!(["Event1", "Event2"])).unwrap(),
TestEventSet::Event1 | TestEventSet::Event2
);
assert_eq!(
serde_json::from_value::<TestEventSet>(json!(["Event1", "AnyNestedEvent"])).unwrap(),
TestEventSet::Event1 | TestEventSet::AnyNestedEvent
);
assert_eq!(
serde_json::from_value::<TestEventSet>(json!(["Event1", "Event2", "AnyNestedEvent"]))
.unwrap(),
TestEventSet::all(),
);

assert_eq!(
serde_json::from_value::<TestEventSet>(json!(["Event1", "Event1", "AnyNestedEvent"]))
.unwrap(),
TestEventSet::Event1 | TestEventSet::AnyNestedEvent,
);
}

#[test]
fn deserialize_invalid() {
assert_eq!(
serde_json::from_value::<TestEventSet>(json!(32))
.unwrap_err()
.to_string(),
"invalid type: integer `32`, expected a sequence of strings"
);

assert_eq!(
serde_json::from_value::<TestEventSet>(json!([32]))
.unwrap_err()
.to_string(),
"invalid type: integer `32`, expected a string"
);

assert_eq!(
serde_json::from_value::<TestEventSet>(json!(["InvalidVariant"]))
.unwrap_err()
.to_string(),
"unknown event variant `InvalidVariant`, expected one of `Event1`, `Event2`, `AnyNestedEvent`"
);

assert_eq!(
serde_json::from_value::<TestEventSet>(json!(["Event1", "Event1", "InvalidVariant"]))
.unwrap_err()
.to_string(),
"unknown event variant `InvalidVariant`, expected one of `Event1`, `Event2`, `AnyNestedEvent`"
);
}

#[test]
fn full_set() {
Expand Down Expand Up @@ -89,3 +172,19 @@ fn event1_or_2_set() {
assert!(event1_or_2_matcher.matches(&TestEvent::Event2));
assert!(!event1_or_2_matcher.matches(&TestEvent::NestedEvent(AnotherEvent)));
}

#[test]
fn repeated() {
assert_eq!(
TestEventSet::Event1 | TestEventSet::Event1,
TestEventSet::Event1
);
assert_eq!(
TestEventSet::Event1 | TestEventSet::Event2 | TestEventSet::Event1,
TestEventSet::Event1 | TestEventSet::Event2
);
assert_eq!(
TestEventSet::all() | TestEventSet::AnyNestedEvent,
TestEventSet::all()
);
}
Binary file modified tools/parity_scale_decoder/samples/trigger.bin
Binary file not shown.
2 changes: 1 addition & 1 deletion tools/parity_scale_decoder/samples/trigger.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"Data": {
"Domain": {
"id_matcher": null,
"event_set": "AnyAccount"
"event_set": ["AnyAccount"]
}
}
},
Expand Down

0 comments on commit d782ec5

Please sign in to comment.