diff --git a/data_model/derive/src/filter.rs b/data_model/derive/src/filter.rs new file mode 100644 index 00000000000..a811246f9da --- /dev/null +++ b/data_model/derive/src/filter.rs @@ -0,0 +1,316 @@ +#![allow( + clippy::expect_used, + clippy::eval_order_dependence, + clippy::unwrap_in_result +)] + +use proc_macro::TokenStream; +use quote::{format_ident, quote}; +use syn::{ + parse::{Parse, ParseStream}, + punctuated::Punctuated, + Attribute, Generics, Ident, Token, Variant, Visibility, +}; + +pub(super) struct EventEnum { + vis: Visibility, + ident: Ident, + generics: Generics, + variants: Punctuated, +} + +pub(super) enum EventVariant { + EventField { variant: Ident, field: Ident }, + IdField(Ident), +} + +impl EventEnum { + /// Used to produce fields like `ByAccount(crate::prelude::FilterOpt)` in `DomainEventFilter`. + fn generate_filter_variants_with_event_fields(&self) -> Vec { + self.variants + .iter() + .filter_map(|variant| match variant { + EventVariant::IdField(_) => None, + EventVariant::EventField { + variant: variant_ident, + field: field_ident, + } => { + // E.g. `Account` field in the event => `ByAccount` in the event filter + let filter_variant_ident = format_ident!("By{}", variant_ident); + // E.g. `AccountEvent` inner field from `Account` variant in event => + // `AccountFilter` inside the event filter + let inner_filter_ident = format_ident!( + "{}Filter", + field_ident + .to_string() + .strip_suffix("Event") + .expect("Variant name should have suffix `Event`"), + ); + let import_path = quote! {crate::prelude}; + Some(quote! { + #filter_variant_ident(#import_path::FilterOpt<#inner_filter_ident>) }) + } + }) + .collect() + } + + /// Used to produce fields like `ByCreated` in `DomainEventFilter`. + fn generate_filter_variants_with_id_fields(&self) -> Vec { + self.variants + .iter() + .filter_map(|variant| match variant { + EventVariant::IdField(event_variant_ident) => { + // Event fields such as `MetadataRemoved` get mapped to `ByMetadataRemoved` + let filter_variant_ident = format_ident!("By{}", event_variant_ident); + Some(filter_variant_ident) + } + EventVariant::EventField { .. } => None, + }) + .collect() + } + + /// Match arms for `Filter` impls of event filters of the form + /// `(Self::ByAccount(filter_opt), crate::prelude::DomainEvent::Account(event)) => {filter_opt.matches(event)}`. + fn generate_filter_impls_with_event_fields(&self) -> Vec { + self.variants + .iter() + .filter_map(|variant| match variant { + EventVariant::IdField(_) => None, + EventVariant::EventField { + variant: event_variant_ident, + .. + } => { + let event_ident = &self.ident; + let filter_variant_ident = format_ident!("By{}", event_variant_ident); + let import_path = quote! {crate::prelude}; + Some(quote! { + (Self::#filter_variant_ident(filter_opt), #import_path::#event_ident::#event_variant_ident(event)) => { + filter_opt.matches(event) + }}) + + }}).collect() + } + + /// Match arms for `Filter` impls of event filters of the form + /// `(Self::ByCreated, crate::prelude::DomainEvent::Created(_))`. + fn generate_filter_impls_with_id_fields(&self) -> Vec { + self.variants + .iter() + .filter_map(|variant| match variant { + EventVariant::IdField(event_variant_ident) => { + let event_ident = &self.ident; + let filter_variant_ident = format_ident!("By{}", event_variant_ident); + let import_path = quote! {crate::prelude}; + Some( + quote! { + (Self::#filter_variant_ident, #import_path::#event_ident::#event_variant_ident(_)) + }) + }, + EventVariant::EventField { .. } => None, + }) + .collect() + } +} + +impl Parse for EventEnum { + fn parse(input: ParseStream) -> syn::Result { + let _attrs = input.call(Attribute::parse_outer)?; + let vis = input.parse()?; + let _enum_token = input.parse::()?; + let ident = input.parse::()?; + let generics = input.parse::()?; + let content; + let _brace_token = syn::braced!(content in input); + let variants = content.parse_terminated(EventVariant::parse)?; + if ident.to_string().ends_with("Event") { + Ok(EventEnum { + vis, + ident, + generics, + variants, + }) + } else { + Err(syn::Error::new_spanned( + ident, + "Bad ident: only derivable for `...Event` enums", + )) + } + } +} + +impl Parse for EventVariant { + fn parse(input: ParseStream) -> syn::Result { + let variant = input.parse::()?; + let variant_ident = variant.ident; + let field_type = variant + .fields + .into_iter() + .next() + .expect("Should have at least one unnamed field") + .ty; + if let syn::Type::Path(path) = field_type { + let field_ident = path + .path + .get_ident() + .expect("Should be an ident-convertible path"); + + if field_ident.to_string().ends_with("Event") { + Ok(EventVariant::EventField { + variant: variant_ident, + field: field_ident.clone(), + }) + } else { + Ok(EventVariant::IdField(variant_ident)) + } + } else { + Err(syn::Error::new_spanned( + field_type, + "Unexpected AST type variant", + )) + } + } +} + +/// Generates the filter for the event. E.g. for `AccountEvent`, `AccountFilter` +/// and its `impl Filter` are generated. +pub(super) fn impl_filter(event: &EventEnum) -> TokenStream { + let EventEnum { + vis, + ident: event_ident, + generics, + .. + } = event; + + let event_filter_and_impl = impl_event_filter(event); + + let filter_ident = format_ident!( + "{}Filter", + event_ident + .to_string() + .strip_suffix("Event") + .expect("Events should follow the naming format") + ); + let event_filter_ident = format_ident!("{}Filter", event_ident); + + let import_path = quote! { crate::prelude }; + let fil_opt = quote! { #import_path::FilterOpt }; + let orig_fil = quote! { #import_path::OriginFilter }; + let imp_event = quote! { #import_path::#event_ident }; + + let filter_doc = format!(" Filter for {} entity", event_ident); + let new_doc = format!(" Construct new {}", filter_ident); + + quote! { + #[derive( + Clone, + PartialEq, + PartialOrd, + Ord, + Eq, + Debug, + Decode, + Encode, + Deserialize, + Serialize, + IntoSchema, + Hash, + )] + #[doc = #filter_doc] + #vis struct #filter_ident #generics { + origin_filter: #fil_opt<#orig_fil<#imp_event>>, + event_filter: #fil_opt<#event_filter_ident> + } + + impl #filter_ident { + #[doc = #new_doc] + pub const fn new( + origin_filter: #fil_opt<#orig_fil<#imp_event>>, + event_filter: #fil_opt<#event_filter_ident>, + ) -> Self { + Self { + origin_filter, + event_filter, + } + } + + /// Get `origin_filter` + #[inline] + pub const fn origin_filter(&self) -> &#fil_opt<#orig_fil<#imp_event>> { + &self.origin_filter + } + + /// Get `event_filter` + #[inline] + pub const fn event_filter(&self) -> &#fil_opt<#event_filter_ident> { + &self.event_filter + } + } + + impl #import_path::Filter for #filter_ident { + type EventType = #imp_event; + fn matches(&self, event: &Self::EventType) -> bool { + self.origin_filter.matches(event) && self.event_filter.matches(event) + } + } + + #event_filter_and_impl + } + .into() +} + +/// Generates the event filter for the event. E.g. for `AccountEvent`, `AccountEventFilter` +/// and its `impl Filter` are generated. +fn impl_event_filter(event: &EventEnum) -> proc_macro2::TokenStream { + let EventEnum { + vis, + ident: event_ident, + generics, + .. + } = event; + + let id_variants = event.generate_filter_variants_with_id_fields(); + let event_variants = event.generate_filter_variants_with_event_fields(); + + let id_impls = event.generate_filter_impls_with_id_fields(); + let event_impls = event.generate_filter_impls_with_event_fields(); + + let event_filter_ident = format_ident!("{}Filter", event_ident); + let import_path = quote! { crate::prelude }; + let imp_event = quote! { #import_path::#event_ident }; + + let event_filter_doc = format!(" Event filter for {} entity", event_ident); + + quote! { + #[derive( + Clone, + PartialEq, + PartialOrd, + Ord, + Eq, + Debug, + Decode, + Encode, + Deserialize, + Serialize, + IntoSchema, + Hash, + )] + #[allow(clippy::enum_variant_names, missing_docs)] + #[doc = #event_filter_doc] + #vis enum #event_filter_ident #generics { + #(#id_variants),*, + #(#event_variants),* + } + + impl #import_path::Filter for #event_filter_ident { + type EventType = #imp_event; + fn matches(&self, event: &#imp_event) -> bool { + match (self, event) { + #(#id_impls)|* => true, + #(#event_impls),* + _ => false, + } + } + } + } +} diff --git a/data_model/derive/src/id.rs b/data_model/derive/src/id.rs new file mode 100644 index 00000000000..fb1a1da9428 --- /dev/null +++ b/data_model/derive/src/id.rs @@ -0,0 +1,120 @@ +#![allow(clippy::expect_used, clippy::eval_order_dependence)] + +use proc_macro::TokenStream; +use quote::quote; +use syn::{ + parse::{Parse, ParseStream}, + Attribute, Field, Generics, Ident, Token, TypePath, Visibility, +}; + +pub(super) struct IdInput { + ident: Ident, + id_type: TypePath, +} + +impl Parse for IdInput { + fn parse(input: ParseStream) -> syn::Result { + let attrs = input.call(Attribute::parse_outer)?; + let id_type = parse_id_attribute(&attrs); + + let _vis = input.parse::()?; + let _struct_token = input.parse::()?; + let ident = input.parse()?; + let _generics = input.parse::()?; + let content; + let _brace_token = syn::braced!(content in input); + let _struct_fields = content.parse_terminated::(Field::parse_named)?; + + Ok(IdInput { ident, id_type }) + } +} + +fn impl_ordeqhash(ast: &IdInput) -> proc_macro2::TokenStream { + let name = &ast.ident; + + quote! { + impl core::cmp::PartialOrd for #name { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } + } + + impl core::cmp::Ord for #name { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + self.id().cmp(other.id()) + } + } + + impl core::cmp::PartialEq for #name { + fn eq(&self, other: &Self) -> bool { + self.id() == other.id() + } + } + + impl core::cmp::Eq for #name {} + + impl core::hash::Hash for #name { + fn hash(&self, state: &mut H) { + self.id().hash(state); + } + } + } +} + +pub(super) fn impl_id(ast: &IdInput) -> TokenStream { + let id = &ast.id_type; + let name = &ast.ident; + + let ordeqhash = impl_ordeqhash(ast); + + let body = if ast.ident.to_string().starts_with("NewRole") { + // `NewRole` struct only has unconventional body + quote! { &self.inner.id } + } else { + // Most usual case for many `data_model` structs + quote! { &self.id } + }; + quote! { + impl Identifiable for #name { + type Id = #id; + + #[inline] + fn id(&self) -> &Self::Id { + #body + } + } + #ordeqhash + } + .into() +} + +/// Find an attribute that is called `id`, parse only the provided +/// literal inside it. E.g. if it is #[id(type = "Id")], only `Id` +/// is extracted. Technically, the first component inside parentheses +/// could be anything with the current implementation. +fn parse_id_attribute(attrs: &[Attribute]) -> TypePath { + attrs + .iter() + .find_map(|attr| { + attr.path.is_ident("id").then(|| match attr.parse_meta() { + Ok(syn::Meta::List(syn::MetaList { nested, .. })) => { + nested.iter().find_map(|m| match m { + syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue { + lit: syn::Lit::Str(inner), + .. + })) => { + let path = inner + .parse::() + .expect("Failed to parse the provided literal"); + Some(path) + } + _ => None, + }) + } + _ => None, + }) + }) + .flatten() + .expect("Should provide a valid type as an attribute to derive `Identifiable`") +} diff --git a/data_model/derive/src/lib.rs b/data_model/derive/src/lib.rs index 030c317e383..32527860bad 100644 --- a/data_model/derive/src/lib.rs +++ b/data_model/derive/src/lib.rs @@ -1,8 +1,83 @@ -//! Crate with `Filter` and `EventFilter` derive macro -#![allow(clippy::expect_used, clippy::panic, clippy::too_many_lines)] +//! A crate containing various derive macros for `data_model` use proc_macro::TokenStream; -use quote::{format_ident, quote}; +use syn::parse_macro_input; + +mod filter; +mod id; + +/// A derive macro for `Identifiable` trait and id-based comparison traits. Currently supports derivations only for +/// `IdBox`, `Event` enums, and structs from the `data_model` crate that don't have generic parameters. +/// +/// As such, the macro introduces a new +/// outer attribute `id` for the entities it is derived from. This attribute should +/// be supplied with the associated type to be used in `impl Identifiable`. The type +/// should be supplied as a string literal that constitutes a +/// legal Rust type path. +/// +/// As this macro also derives an implementation of `Ord`, `PartialOrd`, `Eq`, `PartialEq` and `Hash` traits that always +/// conforms to the same implementation principles based on ids of the entities. +/// Thus none of the entities that derive this macro should derive neither of the aforementioned traits, +/// as they will be overridden. +/// +/// As a rule of thumb, this derive should never be used on any structs that can't be uniquely identifiable, +/// as all the derived traits here rely on the fact of that uniqueness. +/// +/// Example: +/// ```rust +/// +/// // For a struct decorated like this +/// #[derive(IdOrdEqHash)] +/// #[id(type = "Id")] +/// pub struct Domain { +/// /// Identification of this [`Domain`]. +/// id: ::Id, +/// /// [`Account`]s of the domain. +/// accounts: AccountsMap, +/// /// [`Asset`](AssetDefinition)s defined of the `Domain`. +/// asset_definitions: AssetDefinitionsMap, +/// /// IPFS link to the `Domain` logo +/// logo: Option, +/// /// [`Metadata`] of this `Domain` as a key-value store. +/// metadata: Metadata, +/// } +/// +/// // The following impls will be derived +/// impl Identifiable for Domain { +/// type Id = Id; +/// #[inline] +/// fn id(&self) -> &Self::Id { +/// &self.id +/// } +/// } +/// impl core::cmp::PartialOrd for Domain { +/// #[inline] +/// fn partial_cmp(&self, other: &Self) -> Option { +/// Some(self.cmp(other)) +/// } +/// } +/// impl core::cmp::Ord for Domain { +/// fn cmp(&self, other: &Self) -> core::cmp::Ordering { +/// self.id().cmp(other.id()) +/// } +/// } +/// impl core::cmp::PartialEq for Domain { +/// fn eq(&self, other: &Self) -> bool { +/// self.id() == other.id() +/// } +/// } +/// impl core::cmp::Eq for Domain {} +/// impl core::hash::Hash for Domain { +/// fn hash(&self, state: &mut H) { +/// self.id().hash(state); +/// } +/// } +/// ``` +#[proc_macro_derive(IdOrdEqHash, attributes(id))] +pub fn id_derive(input: TokenStream) -> TokenStream { + let ast = parse_macro_input!(input as id::IdInput); + id::impl_id(&ast) +} /// [`Filter`] is used for code generation of `...Filter` structs and `...EventFilter` enums, as well as /// implementing the `Filter` trait for both of them. @@ -14,216 +89,123 @@ use quote::{format_ident, quote}; /// imports. This macro also depends on the naming conventions adopted so far, such as that /// `Event` enums always have tuple variants with either some sort of `Id` or another `Event` inside /// of them, as well as that all `Event` inner fields precede `Id` fields in the enum definition. +/// +/// Example: +/// ```rust +/// // For a struct decorated like this +/// #[derive(Filter)] +/// pub enum DomainEvent { +/// Account(AccountEvent), +/// AssetDefinition(AssetDefinitionEvent), +/// Created(DomainId), +/// Deleted(DomainId), +/// MetadataInserted(DomainId), +/// MetadataRemoved(DomainId), +/// } +/// +/// // The following lengthy code will be derived +/// #[derive( +/// Clone, +/// PartialEq, +/// PartialOrd, +/// Ord, +/// Eq, +/// Debug, +/// Decode, +/// Encode, +/// Deserialize, +/// Serialize, +/// IntoSchema, +/// Hash, +/// )] +/// #[doc = " A filter for DomainFilter"] +/// pub struct DomainFilter { +/// origin_filter: crate::prelude::FilterOpt< +/// crate::prelude::OriginFilter +/// >, +/// event_filter: crate::prelude::FilterOpt, +/// } +/// impl DomainFilter { +/// #[doc = "DomainFilter"] +/// pub const fn new( +/// origin_filter: crate::prelude::FilterOpt< +/// crate::prelude::OriginFilter< +/// >, +/// event_filter: crate::prelude::FilterOpt, +/// ) -> Self { +/// Self { +/// origin_filter, +/// event_filter, +/// } +/// } +/// #[doc = r" Get `origin_filter`"] +/// #[inline] +/// pub const fn origin_filter( +/// &self, +/// ) -> &crate::prelude::FilterOpt< +/// crate::prelude::OriginFilter +/// > { +/// &self.origin_filter +/// } +/// #[doc = r" Get `event_filter`"] +/// #[inline] +/// pub const fn event_filter(&self) -> &crate::prelude::FilterOpt { +/// &self.event_filter +/// } +/// } +/// impl Filter for DomainFilter { +/// type EventType = crate::prelude::DomainEvent; +/// fn matches(&self, event: &Self::EventType) -> bool { +/// self.origin_filter.matches(event) && self.event_filter.matches(event) +/// } +/// } +/// #[derive( +/// Clone, +/// PartialEq, +/// PartialOrd, +/// Ord, +/// Eq, +/// Debug, +/// Decode, +/// Encode, +/// Deserialize, +/// Serialize, +/// IntoSchema, +/// Hash, +/// )] +/// #[allow(clippy::enum_variant_names, missing_docs)] +/// pub enum DomainEventFilter { +/// ByCreated, +/// ByDeleted, +/// ByMetadataInserted, +/// ByMetadataRemoved, +/// ByAccount(crate::prelude::FilterOpt), +/// ByAssetDefinition(crate::prelude::FilterOpt), +/// } +/// impl Filter for DomainEventFilter { +/// type EventType = crate::prelude::DomainEvent; +/// fn matches(&self, event: &crate::prelude::DomainEvent) -> bool { +/// match (self, event) { +/// (Self::ByCreated, crate::prelude::DomainEvent::Created(_)) +/// | (Self::ByDeleted, crate::prelude::DomainEvent::Deleted(_)) +/// | (Self::ByMetadataInserted, crate::prelude::DomainEvent::MetadataInserted(_)) +/// | (Self::ByMetadataRemoved, crate::prelude::DomainEvent::MetadataRemoved(_)) => { +/// true +/// } +/// (Self::ByAccount(filter_opt), crate::prelude::DomainEvent::Account(event)) => { +/// filter_opt.matches(event) +/// } +/// ( +/// Self::ByAssetDefinition(filter_opt), +/// crate::prelude::DomainEvent::AssetDefinition(event), +/// ) => filter_opt.matches(event), +/// _ => false, +/// } +/// } +/// } +/// ``` #[proc_macro_derive(Filter)] pub fn filter_derive(input: TokenStream) -> TokenStream { - let ast = syn::parse(input).expect("Failed to parse input TokenStream."); - impl_from_filter(&ast) -} - -fn impl_from_filter(ast: &syn::DeriveInput) -> TokenStream { - // Omitting attributes as they don't need to be inherited for filters - let syn::DeriveInput { - vis, - ident: event_ident, - generics, - data, - .. - } = ast; - - let event_filter_ident = format_ident!("{}Filter", event_ident); - - let filter_ident = format_ident!( - "{}Filter", - event_ident - .to_string() - .strip_suffix("Event") - .expect("Events should follow the naming format") - ); - - let event_variants = if let syn::Data::Enum(syn::DataEnum { variants, .. }) = data { - variants.iter().collect::>() - } else { - panic!("Only `...Event` enums are supported") - }; - - let mut filter_variants_idents_id_fields = Vec::::new(); - let mut filter_variants_idents_event_fields = Vec::::new(); - - let import_path = quote! { crate::prelude }; - - let filter_variants_event_fields = event_variants - .iter() - .filter_map(|variant| { - let event_filter_variant_ident = format_ident!("By{}", variant.ident); - if let syn::Fields::Unnamed(ref unnamed) = variant.fields { - let variant_type = &unnamed - .unnamed - .first() - .expect("Should have at least one type") - .ty; - process_event_variant( - variant_type, - &event_filter_variant_ident, - &mut filter_variants_idents_id_fields, - &mut filter_variants_idents_event_fields, - ) - .map(|inner_filter_ident| { - Some(quote! { - #event_filter_variant_ident (#import_path::FilterOpt<#inner_filter_ident>) - }) - }) - } else { - None - } - }) - .collect::>(); - - let filter_impl_variant_pairs_event_fields = filter_variants_idents_event_fields - .iter() - .zip(event_variants.iter()) - .map(|(filter_variant_ident, event_var)| { - let event_var_ident = event_var.ident.clone(); - quote! { - (Self::#filter_variant_ident(filter_opt), #import_path::#event_ident::#event_var_ident(event)) => { - filter_opt.matches(event) - } - } - }) - .collect::>(); - - let filter_impl_variant_pairs_id_fields = filter_variants_idents_id_fields - .iter() - .zip( - event_variants - .iter() - .skip(filter_impl_variant_pairs_event_fields.len()), - ) - .map(|(filter_var, event_var)| { - let event_var_ident = format_ident!("{}", &event_var.ident); - quote! { - (Self::#filter_var, #import_path::#event_ident::#event_var_ident (_)) - } - }) - .collect::>(); - - let filter_doc = format!("Filter for {} entity", filter_ident); - let new_doc = format!("Construct new {}", filter_ident); - - quote! { - #[derive( - Clone, - PartialEq, - PartialOrd, - Ord, - Eq, - Debug, - Decode, - Encode, - Deserialize, - Serialize, - IntoSchema, - Hash, - )] - #[doc = #filter_doc] - #vis struct #filter_ident #generics { - origin_filter: #import_path::FilterOpt<#import_path::OriginFilter<#import_path::#event_ident>>, - event_filter: #import_path::FilterOpt<#event_filter_ident> - } - - impl #filter_ident { - #[doc = #new_doc] - pub const fn new( - origin_filter: #import_path::FilterOpt<#import_path::OriginFilter<#import_path::#event_ident>>, - event_filter: #import_path::FilterOpt<#event_filter_ident>, - ) -> Self { - Self { - origin_filter, - event_filter, - } - } - - /// Get `origin_filter` - #[inline] - pub const fn origin_filter(&self) -> &#import_path::FilterOpt<#import_path::OriginFilter<#import_path::#event_ident>> { - &self.origin_filter - } - - /// Get `event_filter` - #[inline] - pub const fn event_filter(&self) -> &#import_path::FilterOpt<#event_filter_ident> { - &self.event_filter - } - } - - impl Filter for #filter_ident { - type EventType = #import_path::#event_ident; - fn matches(&self, event: &Self::EventType) -> bool { - self.origin_filter.matches(event) && self.event_filter.matches(event) - } - } - - #[derive( - Clone, - PartialEq, - PartialOrd, - Ord, - Eq, - Debug, - Decode, - Encode, - Deserialize, - Serialize, - IntoSchema, - Hash, - )] - #[allow(clippy::enum_variant_names, missing_docs)] - #vis enum #event_filter_ident #generics { - #(#filter_variants_idents_id_fields),*, - #(#filter_variants_event_fields),* - } - - impl Filter for #event_filter_ident { - type EventType = #import_path::#event_ident; - fn matches(&self, event: &#import_path::#event_ident) -> bool { - match (self, event) { - #(#filter_impl_variant_pairs_id_fields)|* => true, - #(#filter_impl_variant_pairs_event_fields),* - _ => false, - } - } - } - - } - .into() -} - -fn process_event_variant( - variant_type: &syn::Type, - event_filter_variant_ident: &syn::Ident, - variants_with_id_fields: &mut Vec, - variants_with_event_fields: &mut Vec, -) -> Option { - if let syn::Type::Path(path) = variant_type { - let var_ty_ident = &path.path.segments[0].ident; - - var_ty_ident - .to_string() - .ends_with("Event") - .then(|| { - variants_with_event_fields.push(event_filter_variant_ident.clone()); - format_ident!( - "{}Filter", - var_ty_ident - .to_string() - .strip_suffix("Event") - .expect("Variant name should have suffix `Event`"), - ) - }) - .or_else(|| { - variants_with_id_fields.push(event_filter_variant_ident.clone()); - None - }) - } else { - None - } + let event = parse_macro_input!(input as filter::EventEnum); + filter::impl_filter(&event) } diff --git a/data_model/src/account.rs b/data_model/src/account.rs index 0f4ff75391a..b4cb87e2d43 100644 --- a/data_model/src/account.rs +++ b/data_model/src/account.rs @@ -13,6 +13,7 @@ use std::collections::{btree_map, btree_set}; use derive_more::Display; use getset::{Getters, MutGetters, Setters}; +use iroha_data_model_derive::IdOrdEqHash; #[cfg(feature = "ffi")] use iroha_ffi::{ffi_export, IntoFfi, TryFromFfi}; use iroha_schema::IntoSchema; @@ -127,10 +128,11 @@ impl Default for SignatureCheckCondition { /// Builder which should be submitted in a transaction to create a new [`Account`] #[allow(clippy::multiple_inherent_impl)] #[derive( - Debug, Display, Clone, PartialEq, Eq, Decode, Encode, Deserialize, Serialize, IntoSchema, + Debug, Display, Clone, IdOrdEqHash, Decode, Encode, Deserialize, Serialize, IntoSchema, )] #[cfg_attr(feature = "ffi", derive(IntoFfi, TryFromFfi))] #[display(fmt = "[{id}]")] +#[id(type = "::Id")] pub struct NewAccount { /// Identification id: ::Id, @@ -159,20 +161,6 @@ impl Registrable for NewAccount { } } -impl PartialOrd for NewAccount { - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for NewAccount { - #[inline] - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.id.cmp(&other.id) - } -} - impl HasMetadata for NewAccount { fn metadata(&self) -> &Metadata { &self.metadata @@ -212,8 +200,7 @@ impl NewAccount { Debug, Display, Clone, - PartialEq, - Eq, + IdOrdEqHash, Getters, MutGetters, Setters, @@ -225,6 +212,7 @@ impl NewAccount { )] #[cfg_attr(feature = "ffi", derive(IntoFfi, TryFromFfi))] #[display(fmt = "({id})")] // TODO: Add more? +#[id(type = "Id")] #[allow(clippy::multiple_inherent_impl)] pub struct Account { /// An Identification of the [`Account`]. @@ -246,14 +234,6 @@ pub struct Account { roles: RoleIds, } -impl Identifiable for Account { - type Id = Id; - - fn id(&self) -> &Self::Id { - &self.id - } -} - impl HasMetadata for Account { fn metadata(&self) -> &Metadata { &self.metadata @@ -264,20 +244,6 @@ impl Registered for Account { type With = NewAccount; } -impl PartialOrd for Account { - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Account { - #[inline] - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.id().cmp(other.id()) - } -} - #[cfg_attr(feature = "ffi", ffi_export)] impl Account { /// Construct builder for [`Account`] identifiable by [`Id`] containing the given signatories. diff --git a/data_model/src/asset.rs b/data_model/src/asset.rs index 45a84f07def..e5c9133baec 100644 --- a/data_model/src/asset.rs +++ b/data_model/src/asset.rs @@ -9,6 +9,7 @@ use std::collections::btree_map; use derive_more::Display; use getset::{Getters, MutGetters}; +use iroha_data_model_derive::IdOrdEqHash; #[cfg(feature = "ffi")] use iroha_ffi::{ffi_export, IntoFfi, TryFromFfi}; use iroha_macro::FromVariant; @@ -116,8 +117,7 @@ impl AssetDefinitionEntry { Debug, Display, Clone, - PartialEq, - Eq, + IdOrdEqHash, Getters, MutGetters, Decode, @@ -129,6 +129,7 @@ impl AssetDefinitionEntry { #[cfg_attr(feature = "ffi", derive(IntoFfi, TryFromFfi))] #[allow(clippy::multiple_inherent_impl)] #[display(fmt = "{id} {value_type}{mintable}")] +#[id(type = "DefinitionId")] pub struct AssetDefinition { /// An Identification of the [`AssetDefinition`]. id: ::Id, @@ -149,20 +150,6 @@ impl HasMetadata for AssetDefinition { } } -impl PartialOrd for AssetDefinition { - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for AssetDefinition { - #[inline] - fn cmp(&self, other: &Self) -> Ordering { - self.id().cmp(other.id()) - } -} - /// An assets mintability scheme. `Infinitely` means elastic /// supply. `Once` is what you want to use. Don't use `Not` explicitly /// outside of smartcontracts. @@ -199,22 +186,13 @@ pub enum Mintable { /// Asset represents some sort of commodity or value. /// All possible variants of [`Asset`] entity's components. #[derive( - Debug, - Display, - Clone, - PartialEq, - Eq, - Getters, - Decode, - Encode, - Deserialize, - Serialize, - IntoSchema, + Debug, Display, Clone, IdOrdEqHash, Getters, Decode, Encode, Deserialize, Serialize, IntoSchema, )] #[cfg_attr(feature = "ffi", derive(IntoFfi, TryFromFfi))] #[cfg_attr(feature = "ffi", ffi_export)] #[getset(get = "pub")] #[display(fmt = "{id}: {value}")] +#[id(type = "Id")] pub struct Asset { /// Component Identification. #[getset(skip)] @@ -308,20 +286,6 @@ impl AssetValue { } } -impl PartialOrd for Asset { - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Asset { - #[inline] - fn cmp(&self, other: &Self) -> Ordering { - self.id().cmp(other.id()) - } -} - macro_rules! impl_try_as_for_asset_value { ( $($variant:ident( $ty:ty ),)* ) => {$( impl TryAsMut<$ty> for AssetValue { @@ -418,10 +382,11 @@ pub struct Id { /// Builder which can be submitted in a transaction to create a new [`AssetDefinition`] #[allow(clippy::multiple_inherent_impl)] #[derive( - Debug, Display, Clone, PartialEq, Eq, Decode, Encode, Deserialize, Serialize, IntoSchema, + Debug, Display, Clone, IdOrdEqHash, Decode, Encode, Deserialize, Serialize, IntoSchema, )] #[cfg_attr(feature = "ffi", derive(IntoFfi, TryFromFfi))] #[display(fmt = "{id} {mintable}{value_type}")] +#[id(type = "::Id")] pub struct NewAssetDefinition { id: ::Id, value_type: AssetValueType, @@ -451,20 +416,6 @@ impl HasMetadata for NewAssetDefinition { } } -impl PartialOrd for NewAssetDefinition { - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for NewAssetDefinition { - #[inline] - fn cmp(&self, other: &Self) -> Ordering { - self.id.cmp(&other.id) - } -} - impl NewAssetDefinition { /// Create a [`NewAssetDefinition`], reserved for internal use. fn new(id: ::Id, value_type: AssetValueType) -> Self { @@ -613,26 +564,10 @@ impl Id { } } -impl Identifiable for Asset { - type Id = Id; - - fn id(&self) -> &Self::Id { - &self.id - } -} - impl Registered for Asset { type With = Self; } -impl Identifiable for AssetDefinition { - type Id = DefinitionId; - - fn id(&self) -> &Self::Id { - &self.id - } -} - impl Registered for AssetDefinition { type With = NewAssetDefinition; } diff --git a/data_model/src/domain.rs b/data_model/src/domain.rs index 86711588630..0aac184f2f9 100644 --- a/data_model/src/domain.rs +++ b/data_model/src/domain.rs @@ -7,11 +7,12 @@ #[cfg(not(feature = "std"))] use alloc::{format, string::String, vec::Vec}; -use core::{cmp::Ordering, str::FromStr}; +use core::str::FromStr; use derive_more::{Display, FromStr}; use getset::{Getters, MutGetters}; use iroha_crypto::PublicKey; +use iroha_data_model_derive::IdOrdEqHash; #[cfg(feature = "ffi")] use iroha_ffi::{ffi_export, IntoFfi, TryFromFfi}; use iroha_primitives::conststr::ConstString; @@ -72,17 +73,18 @@ impl From for Domain { /// Builder which can be submitted in a transaction to create a new [`Domain`] #[derive( - Debug, Display, Clone, PartialEq, Eq, Decode, Encode, Deserialize, Serialize, IntoSchema, + Debug, Display, Clone, IdOrdEqHash, Decode, Encode, Deserialize, Serialize, IntoSchema, )] #[cfg_attr(feature = "ffi", derive(IntoFfi, TryFromFfi))] #[allow(clippy::multiple_inherent_impl)] #[display(fmt = "[{id}]")] +#[id(type = "::Id")] pub struct NewDomain { - /// The identification associated to the domain builder. + /// The identification associated with the domain builder. id: ::Id, /// The (IPFS) link to the logo of this domain. logo: Option, - /// metadata associated to the domain builder. + /// Metadata associated with the domain builder. metadata: Metadata, } @@ -110,20 +112,6 @@ impl HasMetadata for NewDomain { } } -impl PartialOrd for NewDomain { - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for NewDomain { - #[inline] - fn cmp(&self, other: &Self) -> Ordering { - self.id.cmp(&other.id) - } -} - impl NewDomain { /// Create a [`NewDomain`], reserved for internal use. #[must_use] @@ -163,8 +151,7 @@ impl NewDomain { Debug, Display, Clone, - PartialEq, - Eq, + IdOrdEqHash, Getters, MutGetters, Decode, @@ -177,6 +164,7 @@ impl NewDomain { #[cfg_attr(feature = "ffi", ffi_export)] #[allow(clippy::multiple_inherent_impl)] #[display(fmt = "[{id}]")] +#[id(type = "Id")] pub struct Domain { /// Identification of this [`Domain`]. id: ::Id, @@ -201,32 +189,10 @@ impl HasMetadata for Domain { } } -impl Identifiable for Domain { - type Id = Id; - - fn id(&self) -> &Self::Id { - &self.id - } -} - impl Registered for Domain { type With = NewDomain; } -impl PartialOrd for Domain { - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Domain { - #[inline] - fn cmp(&self, other: &Self) -> Ordering { - self.id().cmp(other.id()) - } -} - #[cfg_attr(feature = "ffi", ffi_export)] impl Domain { /// Construct builder for [`Domain`] identifiable by [`Id`]. diff --git a/data_model/src/peer.rs b/data_model/src/peer.rs index ccfc751c97d..8df1c129b2d 100644 --- a/data_model/src/peer.rs +++ b/data_model/src/peer.rs @@ -8,6 +8,7 @@ use core::{ }; use derive_more::Display; +use iroha_data_model_derive::IdOrdEqHash; use iroha_schema::IntoSchema; use parity_scale_codec::{Decode, Encode}; use serde::{Deserialize, Serialize}; @@ -16,20 +17,10 @@ use crate::{Identifiable, PublicKey, Registered, Value}; /// Peer represents Iroha instance. #[derive( - Debug, - Display, - Clone, - PartialEq, - Eq, - PartialOrd, - Ord, - Decode, - Encode, - Deserialize, - Serialize, - IntoSchema, + Debug, Display, Clone, IdOrdEqHash, Decode, Encode, Deserialize, Serialize, IntoSchema, )] #[display(fmt = "@@{}", "id.address")] +#[id(type = "Id")] pub struct Peer { /// Peer Identification. pub id: ::Id, @@ -83,14 +74,6 @@ impl Peer { } } -impl Identifiable for Peer { - type Id = Id; - - fn id(&self) -> &Self::Id { - &self.id - } -} - impl Registered for Peer { type With = Self; } diff --git a/data_model/src/role.rs b/data_model/src/role.rs index e3e6e76442c..514a77a57be 100644 --- a/data_model/src/role.rs +++ b/data_model/src/role.rs @@ -7,6 +7,7 @@ use std::collections::btree_set; use derive_more::{Constructor, Display, FromStr}; use getset::Getters; +use iroha_data_model_derive::IdOrdEqHash; #[cfg(feature = "ffi")] use iroha_ffi::{ffi_export, IntoFfi, TryFromFfi}; use iroha_schema::IntoSchema; @@ -47,22 +48,13 @@ pub struct Id { /// Role is a tag for a set of permission tokens. #[derive( - Debug, - Display, - Clone, - PartialEq, - Eq, - Getters, - Decode, - Encode, - Deserialize, - Serialize, - IntoSchema, + Debug, Display, Clone, IdOrdEqHash, Getters, Decode, Encode, Deserialize, Serialize, IntoSchema, )] #[cfg_attr(feature = "ffi", derive(IntoFfi, TryFromFfi))] #[cfg_attr(feature = "ffi", ffi_export)] #[display(fmt = "{id}")] #[getset(get = "pub")] +#[id(type = "Id")] pub struct Role { /// Unique name of the role. #[getset(skip)] @@ -72,20 +64,6 @@ pub struct Role { permissions: Permissions, } -impl PartialOrd for Role { - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Role { - #[inline] - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.id().cmp(other.id()) - } -} - #[cfg_attr(feature = "ffi", ffi_export)] impl Role { /// Constructor. @@ -101,34 +79,17 @@ impl Role { } } -impl Identifiable for Role { - type Id = Id; - - fn id(&self) -> &Self::Id { - &self.id - } -} - impl Registered for Role { type With = NewRole; } /// Builder for [`Role`] #[derive( - Debug, - Display, - Clone, - PartialEq, - Eq, - Getters, - Decode, - Encode, - Deserialize, - Serialize, - IntoSchema, + Debug, Display, Clone, IdOrdEqHash, Getters, Decode, Encode, Deserialize, Serialize, IntoSchema, )] #[cfg_attr(feature = "ffi", derive(IntoFfi, TryFromFfi))] #[allow(clippy::multiple_inherent_impl)] +#[id(type = "::Id")] pub struct NewRole { inner: Role, } @@ -144,20 +105,6 @@ impl crate::Registrable for NewRole { } } -impl PartialOrd for NewRole { - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for NewRole { - #[inline] - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.inner.cmp(&other.inner) - } -} - #[cfg_attr(feature = "ffi", ffi_export)] impl NewRole { /// Add permission to the [`Role`]