diff --git a/data_model/derive/src/lib.rs b/data_model/derive/src/lib.rs index a5c8d84512d..eb8bef08199 100644 --- a/data_model/derive/src/lib.rs +++ b/data_model/derive/src/lib.rs @@ -1,8 +1,275 @@ -//! 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::{Parse, ParseStream}, + parse_macro_input, + punctuated::Punctuated, + token::Brace, + Attribute, Field, Generics, Ident, Token, TypePath, Variant, Visibility, +}; + +struct IdInput { + _attrs: Vec, + _vis: Visibility, + _enum_token: Option, + _struct_token: Option, + ident: Ident, + _generics: Generics, + _brace_token: Brace, + _enum_variants: Option>, + _struct_fields: Option>, + event_variants: Option>, + id_type: TypePath, +} + +struct EventEnum { + _attrs: Vec, + vis: Visibility, + _enum_token: Token![enum], + ident: Ident, + generics: Generics, + _brace_token: Brace, + variants: Punctuated, +} + +enum EventVariant { + EventField { variant: Ident, field: Ident }, + IdField(Ident), +} + +impl EventEnum { + 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, + } => { + let filter_variant_ident = format_ident!("By{}", variant_ident); + 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() + } + + fn generate_filter_variants_with_id_fields(&self) -> Vec { + self.variants + .iter() + .filter_map(|variant| match variant { + EventVariant::IdField(event_variant_ident) => { + let filter_variant_ident = format_ident!("By{}", event_variant_ident); + Some(filter_variant_ident) + } + EventVariant::EventField { .. } => None, + }) + .collect() + } + + 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.clone(); + 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() + } + + 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.clone(); + 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 IdInput { + fn generate_identifiable_impls_with_event_fields( + &self, + id_type: &TypePath, + ) -> Vec { + self.event_variants + .as_ref() + .expect("Should not be called for enums/structs besides `Event`") + .iter() + .filter_map(|variant| match variant { + EventVariant::IdField(_) => None, + EventVariant::EventField { + variant: event_variant_ident, + .. + } => { + let inner_event_field_ident = format_ident!( + "{}_id", + id_type + .path + .get_ident() + .expect("Should be good") + .to_string() + .strip_suffix("Id") + .expect("Associated type for `Event`s should have `Id` suffix") + .to_ascii_lowercase() + ); + Some(quote! { + Self::#event_variant_ident(event) => &event.id().#inner_event_field_ident + }) + } + }) + .collect() + } + + fn generate_identifiable_impls_with_id_fields(&self) -> Vec { + self.event_variants + .as_ref() + .expect("Should not be called for enums/structs besides `Event`") + .iter() + .filter_map(|variant| match variant { + EventVariant::IdField(event_variant_ident) => Some(quote! { + Self::#event_variant_ident(id) + }), + EventVariant::EventField { .. } => None, + }) + .collect() + } +} +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::()?; + if input.peek(Token![struct]) { + let _struct_token = Some(input.parse::()?); + // let _variants = + let content; + Ok(IdInput { + _attrs, + _vis, + _enum_token: None, + _struct_token, + ident: input.parse()?, + _generics: input.parse()?, + _brace_token: syn::braced!(content in input), + _struct_fields: Some(content.parse_terminated(Field::parse_named)?), + _enum_variants: None, + event_variants: None, + id_type, + }) + } else { + let _enum_token = Some(input.parse::()?); + let ident = input.parse::()?; + let content; + if ident.to_string().ends_with("Event") { + Ok(IdInput { + _attrs, + _vis, + _enum_token, + _struct_token: None, + ident, + _generics: input.parse()?, + _brace_token: syn::braced!(content in input), + _enum_variants: None, + _struct_fields: None, + event_variants: Some(content.parse_terminated(EventVariant::parse)?), + id_type, + }) + } else { + // Box case + Ok(IdInput { + _attrs, + _vis, + _enum_token, + _struct_token: None, + ident, + _generics: input.parse()?, + _brace_token: syn::braced!(content in input), + _enum_variants: Some(content.parse_terminated(syn::Variant::parse)?), + _struct_fields: None, + event_variants: None, + id_type, + }) + } + } + } +} +impl Parse for EventEnum { + fn parse(input: ParseStream) -> syn::Result { + let content; + let event = EventEnum { + _attrs: input.call(Attribute::parse_outer)?, + vis: input.parse()?, + _enum_token: input.parse()?, + ident: input.parse()?, + generics: input.parse()?, + _brace_token: syn::braced!(content in input), + variants: content.parse_terminated(EventVariant::parse)?, + }; + if !event.ident.to_string().ends_with("Event") { + Err(syn::Error::new_spanned(event.ident, "Bad enum ident")) + } else { + Ok(event) + } + } +} + +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 named"); + + 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, "Wrong type path")) + } + } +} /// [`Filter`] is used for code generation of `...Filter` structs and `...EventFilter` enums, as well as /// implementing the `Filter` trait for both of them. @@ -16,21 +283,22 @@ use quote::{format_ident, quote}; /// of them, as well as that all `Event` inner fields precede `Id` fields in the enum definition. #[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) + let event = parse_macro_input!(input as EventEnum); + impl_filter(&event) } -fn impl_from_filter(ast: &syn::DeriveInput) -> TokenStream { - // Omitting attributes as they don't need to be inherited for filters - let syn::DeriveInput { +fn impl_filter(event: &EventEnum) -> TokenStream { + let EventEnum { vis, ident: event_ident, generics, - data, .. - } = ast; + } = event; + let id_variants = event.generate_filter_variants_with_id_fields(); + let event_variants = event.generate_filter_variants_with_event_fields(); - let event_filter_ident = format_ident!("{}Filter", event_ident); + let id_impls = event.generate_filter_impls_with_id_fields(); + let event_impls = event.generate_filter_impls_with_event_fields(); let filter_ident = format_ident!( "{}Filter", @@ -39,75 +307,15 @@ fn impl_from_filter(ast: &syn::DeriveInput) -> TokenStream { .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 event_filter_ident = format_ident!("{}Filter", event_ident); let import_path = quote! { crate::prelude }; + let fil_opt = quote! { #import_path::FilterOpt }; + let id_fil = quote! { #import_path::IdFilter }; + let imp_event = quote! { #import_path::#event_ident }; - 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); + let filter_doc = format!("{}", filter_ident); + let new_doc = format!("{}", filter_ident); quote! { #[derive( @@ -126,15 +334,15 @@ fn impl_from_filter(ast: &syn::DeriveInput) -> TokenStream { )] #[doc = #filter_doc] #vis struct #filter_ident #generics { - id_filter: #import_path::FilterOpt<#import_path::IdFilter<<#import_path::#event_ident as Identifiable>::Id>>, - event_filter: #import_path::FilterOpt<#event_filter_ident> + id_filter: #fil_opt<#id_fil<<#imp_event as Identifiable>::Id>>, + event_filter: #fil_opt<#event_filter_ident> } impl #filter_ident { #[doc = #new_doc] pub const fn new( - id_filter: #import_path::FilterOpt<#import_path::IdFilter<<#import_path::#event_ident as Identifiable>::Id>>, - event_filter: #import_path::FilterOpt<#event_filter_ident>, + id_filter: #fil_opt<#id_fil<<#imp_event as Identifiable>::Id>>, + event_filter: #fil_opt<#event_filter_ident>, ) -> Self { Self { id_filter, @@ -144,19 +352,19 @@ fn impl_from_filter(ast: &syn::DeriveInput) -> TokenStream { /// Get `id_filter` #[inline] - pub const fn id_filter(&self) -> &#import_path::FilterOpt<#import_path::IdFilter<<#import_path::#event_ident as Identifiable>::Id>> { + pub const fn id_filter(&self) -> &#fil_opt<#id_fil<<#imp_event as Identifiable>::Id>> { &self.id_filter } /// Get `event_filter` #[inline] - pub const fn event_filter(&self) -> &#import_path::FilterOpt<#event_filter_ident> { + pub const fn event_filter(&self) -> &#fil_opt<#event_filter_ident> { &self.event_filter } } impl Filter for #filter_ident { - type EventType = #import_path::#event_ident; + type EventType = #imp_event; fn matches(&self, entity: &Self::EventType) -> bool { self.id_filter.matches(entity.id()) && self.event_filter.matches(entity) } @@ -178,16 +386,16 @@ fn impl_from_filter(ast: &syn::DeriveInput) -> TokenStream { )] #[allow(clippy::enum_variant_names, missing_docs)] #vis enum #event_filter_ident #generics { - #(#filter_variants_idents_id_fields),*, - #(#filter_variants_event_fields),* + #(#id_variants),*, + #(#event_variants),* } impl Filter for #event_filter_ident { - type EventType = #import_path::#event_ident; - fn matches(&self, event: &#import_path::#event_ident) -> bool { + type EventType = #imp_event; + fn matches(&self, event: &#imp_event) -> bool { match (self, event) { - #(#filter_impl_variant_pairs_id_fields)|* => true, - #(#filter_impl_variant_pairs_event_fields),* + #(#id_impls)|* => true, + #(#event_impls),* _ => false, } } @@ -197,33 +405,178 @@ fn impl_from_filter(ast: &syn::DeriveInput) -> TokenStream { .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; +/// 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 this derive should only be used for types that also implement +/// `Identifiable`. +#[proc_macro_derive(OrdEqHash)] +pub fn ordeqhash_derive(input: TokenStream) -> TokenStream { + let ast = syn::parse(input).expect("Failed to parse input TokenStream."); + impl_ordeqhash(&ast) +} - 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 - }) +fn impl_ordeqhash(ast: &syn::DeriveInput) -> TokenStream { + let name = ast.ident.clone(); + + quote! { + impl PartialOrd for #name { + #[cfg(feature = "std")] + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } + #[cfg(not(feature = "std"))] + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } + } + + impl Ord for #name { + #[cfg(feature = "std")] + #[inline] + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.id().cmp(other.id()) + } + #[cfg(not(feature = "std"))] + #[inline] + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + self.id().cmp(other.id()) + } + } + + impl PartialEq for #name { + fn eq(&self, other: &Self) -> bool { + self.id() == other.id() + } + } + + impl Eq for #name {} + + #[cfg(feature = "std")] + impl std::hash::Hash for #name { + fn hash(&self, state: &mut H) { + self.id().hash(state); + } + } + + #[cfg(not(feature = "std"))] + impl core::hash::Hash for #name { + fn hash(&self, state: &mut H) { + self.id().hash(state); + } + } + } + .into() +} + +/// A derive macro for `Identifiable` trait. Currently supports derivations only for +/// `IdBox`, `Event` enums and structs from the `data_model` crate such as `Domain` +/// or `NewDomain`. Only `NewRole` is unsupported among the latter as it has a somewhat +/// unconventional implementation of the trait. +/// +/// As such, the macro introduces a new +/// outer attribute `id` for the entities it is derived upon. This attribute should +/// be supplied with the associated type to be used in `impl Identifiable`. The type +/// should be supplied as a string literal. The string literal should constitute a +/// legal Rust type path for that reason. +/// +/// Example: +/// ```rust +/// +/// // For a struct decorated like this +/// #[derive(Identifiable)] +/// #[id(type = "::Id")] +/// pub struct NewDomain { +/// /// The identification associated to the domain builder. +/// id: ::Id, +/// /// The (IPFS) link to the logo of this domain. +/// logo: Option, +/// /// metadata associated to the domain builder. +/// metadata: Metadata, +/// } +/// +/// // The following impl will be derived +/// impl Identifiable for NewDomain { +/// type Id = ::Id; +/// +/// fn id(&self) -> &Self::Id { +/// &self.id +/// } +/// } +/// ``` +#[proc_macro_derive(Identifiable, attributes(id))] +pub fn id_derive(input: TokenStream) -> TokenStream { + let ast = parse_macro_input!(input as IdInput); + impl_id(&ast) +} + +fn impl_id(ast: &IdInput) -> TokenStream { + let id = ast.id_type.clone(); + let name = ast.ident.clone(); + + if ast.event_variants.is_some() { + let event_fields = ast.generate_identifiable_impls_with_event_fields(&ast.id_type); + let id_fields = ast.generate_identifiable_impls_with_id_fields(); + + quote! { + impl Identifiable for #name { + type Id = #id; + + fn id(&self) -> &Self::Id { + match self { + #(#id_fields)|* => id, + #(#event_fields),* + } + } + } + } + .into() + } else if ast._struct_fields.is_some() { + quote! { + impl Identifiable for #name { + type Id = #id; + + fn id(&self) -> &Self::Id { + &self.id + } + } + } + .into() } else { - None + quote! { + impl Identifiable for #name { + type Id = #id; + + fn id(&self) -> &Self::Id { + self + } + } + } + .into() } } + +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 parse lit?"); + Some(path) + } + _ => None, + }) + } + _ => None, + }) + }) + .flatten() + .expect("Should provide a valid type as an attribute to derive `Identifiable`") +} diff --git a/data_model/src/account.rs b/data_model/src/account.rs index 2f3b91981b7..bb70c8078c9 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::OrdEqHash; #[cfg(feature = "ffi_api")] use iroha_ffi::ffi_bindgen; use iroha_schema::IntoSchema; @@ -124,9 +125,19 @@ 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, + OrdEqHash, + Decode, + Encode, + Deserialize, + Serialize, + IntoSchema, + Identifiable, )] #[display(fmt = "[{id}]")] +#[id(type = "::Id")] pub struct NewAccount { /// Identification id: ::Id, @@ -136,28 +147,6 @@ pub struct NewAccount { metadata: Metadata, } -impl Identifiable for NewAccount { - type Id = ::Id; - - fn id(&self) -> &Self::Id { - &self.id - } -} - -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 @@ -207,8 +196,7 @@ impl NewAccount { Debug, Display, Clone, - PartialEq, - Eq, + OrdEqHash, Getters, MutGetters, Setters, @@ -217,10 +205,12 @@ impl NewAccount { Deserialize, Serialize, IntoSchema, + Identifiable, )] #[allow(clippy::multiple_inherent_impl)] #[cfg_attr(feature = "ffi_api", ffi_bindgen)] #[display(fmt = "({id})")] // TODO: Add more? +#[id(type = "Id")] pub struct Account { /// An Identification of the [`Account`]. id: ::Id, @@ -240,14 +230,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 @@ -258,20 +240,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_api", ffi_bindgen)] 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 54a40c0e66e..7bba3a2194e 100644 --- a/data_model/src/asset.rs +++ b/data_model/src/asset.rs @@ -4,6 +4,7 @@ #[cfg(not(feature = "std"))] use alloc::{collections::btree_map, format, string::String, vec::Vec}; use core::{cmp::Ordering, str::FromStr}; +use iroha_data_model_derive::OrdEqHash; #[cfg(feature = "std")] use std::collections::btree_map; @@ -112,8 +113,7 @@ impl AssetDefinitionEntry { Debug, Display, Clone, - PartialEq, - Eq, + OrdEqHash, Getters, MutGetters, Setters, @@ -122,10 +122,12 @@ impl AssetDefinitionEntry { Deserialize, Serialize, IntoSchema, + Identifiable, )] #[allow(clippy::multiple_inherent_impl)] #[cfg_attr(feature = "ffi_api", iroha_ffi::ffi_bindgen)] #[display(fmt = "{id} {value_type}{mintable}")] +#[id(type = "DefinitionId")] pub struct AssetDefinition { /// An Identification of the [`AssetDefinition`]. id: ::Id, @@ -146,20 +148,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. @@ -197,18 +185,19 @@ pub enum Mintable { Debug, Display, Clone, - PartialEq, - Eq, + OrdEqHash, Getters, Decode, Encode, Deserialize, Serialize, IntoSchema, + Identifiable, )] #[getset(get = "pub")] #[cfg_attr(feature = "ffi_api", iroha_ffi::ffi_bindgen)] #[display(fmt = "{id}: {value}")] +#[id(type = "Id")] pub struct Asset { /// Component Identification. #[getset(skip)] @@ -298,20 +287,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 { @@ -406,9 +381,20 @@ 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, + PartialEq, + Eq, + Decode, + Encode, + Deserialize, + Serialize, + IntoSchema, + Identifiable, )] #[display(fmt = "{id} {mintable}{value_type}")] +#[id(type = "::Id")] pub struct NewAssetDefinition { id: ::Id, value_type: AssetValueType, @@ -416,14 +402,6 @@ pub struct NewAssetDefinition { metadata: Metadata, } -impl Identifiable for NewAssetDefinition { - type Id = ::Id; - - fn id(&self) -> &Self::Id { - &self.id - } -} - impl HasMetadata for NewAssetDefinition { fn metadata(&self) -> &Metadata { &self.metadata @@ -599,26 +577,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 217d3277740..a7e9c60f4b5 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::OrdEqHash; #[cfg(feature = "ffi_api")] use iroha_ffi::ffi_bindgen; use iroha_schema::IntoSchema; @@ -71,10 +72,20 @@ 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, + OrdEqHash, + Decode, + Encode, + Deserialize, + Serialize, + IntoSchema, + Identifiable, )] #[allow(clippy::multiple_inherent_impl)] #[display(fmt = "[{id}]")] +#[id(type = "::Id")] pub struct NewDomain { /// The identification associated to the domain builder. id: ::Id, @@ -91,20 +102,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] @@ -147,21 +144,12 @@ impl NewDomain { } } -impl Identifiable for NewDomain { - type Id = ::Id; - - fn id(&self) -> &Self::Id { - &self.id - } -} - /// Named group of [`Account`] and [`Asset`](`crate::asset::Asset`) entities. #[derive( Debug, Display, Clone, - PartialEq, - Eq, + OrdEqHash, Getters, MutGetters, Decode, @@ -169,10 +157,12 @@ impl Identifiable for NewDomain { Deserialize, Serialize, IntoSchema, + Identifiable, )] #[allow(clippy::multiple_inherent_impl)] #[cfg_attr(feature = "ffi_api", ffi_bindgen)] #[display(fmt = "[{id}]")] +#[id(type = "Id")] pub struct Domain { /// Identification of this [`Domain`]. id: ::Id, @@ -195,32 +185,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_api", ffi_bindgen)] impl Domain { /// Construct builder for [`Domain`] identifiable by [`Id`]. diff --git a/data_model/src/events/data/events.rs b/data_model/src/events/data/events.rs index 027b30b7c6f..7a3e0400ea7 100644 --- a/data_model/src/events/data/events.rs +++ b/data_model/src/events/data/events.rs @@ -11,10 +11,21 @@ mod asset { use super::*; #[derive( - Clone, PartialEq, Eq, Debug, Decode, Encode, Deserialize, Serialize, IntoSchema, Filter, + Clone, + PartialEq, + Eq, + Debug, + Decode, + Encode, + Deserialize, + Serialize, + IntoSchema, + Filter, + Identifiable, )] #[non_exhaustive] #[allow(missing_docs)] + #[id(type = "AssetId")] pub enum AssetEvent { Created(AssetId), Deleted(AssetId), @@ -24,26 +35,22 @@ mod asset { MetadataRemoved(AssetId), } - impl Identifiable for AssetEvent { - type Id = AssetId; - - fn id(&self) -> &AssetId { - match self { - Self::Created(id) - | Self::Deleted(id) - | Self::Added(id) - | Self::Removed(id) - | Self::MetadataInserted(id) - | Self::MetadataRemoved(id) => id, - } - } - } - #[derive( - Clone, PartialEq, Eq, Debug, Decode, Encode, Deserialize, Serialize, IntoSchema, Filter, + Clone, + PartialEq, + Eq, + Debug, + Decode, + Encode, + Deserialize, + Serialize, + IntoSchema, + Filter, + Identifiable, )] #[non_exhaustive] #[allow(missing_docs)] + #[id(type = "AssetDefinitionId")] pub enum AssetDefinitionEvent { Created(AssetDefinitionId), MintabilityChanged(AssetDefinitionId), @@ -51,23 +58,6 @@ mod asset { MetadataInserted(AssetDefinitionId), MetadataRemoved(AssetDefinitionId), } - // NOTE: Whenever you add a new event here, please also update the - // AssetDefinitionEventFilter enum and its `impl Filter for - // AssetDefinitionEventFilter`. - - impl Identifiable for AssetDefinitionEvent { - type Id = AssetDefinitionId; - - fn id(&self) -> &AssetDefinitionId { - match self { - Self::Created(id) - | Self::Deleted(id) - | Self::MintabilityChanged(id) - | Self::MetadataInserted(id) - | Self::MetadataRemoved(id) => id, - } - } - } } mod peer { @@ -76,24 +66,25 @@ mod peer { use super::*; #[derive( - Clone, PartialEq, Eq, Debug, Decode, Encode, Deserialize, Serialize, IntoSchema, Filter, + Clone, + PartialEq, + Eq, + Debug, + Decode, + Encode, + Deserialize, + Serialize, + IntoSchema, + Filter, + Identifiable, )] #[non_exhaustive] #[allow(missing_docs)] + #[id(type = "PeerId")] pub enum PeerEvent { Added(PeerId), Removed(PeerId), } - - impl Identifiable for PeerEvent { - type Id = PeerId; - - fn id(&self) -> &PeerId { - match self { - Self::Added(id) | Self::Removed(id) => id, - } - } - } } mod role { @@ -102,24 +93,25 @@ mod role { use super::*; #[derive( - Clone, PartialEq, Eq, Debug, Decode, Encode, Deserialize, Serialize, IntoSchema, Filter, + Clone, + PartialEq, + Eq, + Debug, + Decode, + Encode, + Deserialize, + Serialize, + IntoSchema, + Filter, + Identifiable, )] #[non_exhaustive] #[allow(missing_docs)] + #[id(type = "RoleId")] pub enum RoleEvent { Created(RoleId), Deleted(RoleId), } - - impl Identifiable for RoleEvent { - type Id = RoleId; - - fn id(&self) -> &RoleId { - match self { - Self::Created(id) | Self::Deleted(id) => id, - } - } - } } mod account { @@ -129,10 +121,21 @@ mod account { /// Account event #[derive( - Clone, PartialEq, Eq, Debug, Decode, Encode, Deserialize, Serialize, IntoSchema, Filter, + Clone, + PartialEq, + Eq, + Debug, + Decode, + Encode, + Deserialize, + Serialize, + IntoSchema, + Filter, + Identifiable, )] #[non_exhaustive] #[allow(missing_docs)] + #[id(type = "AccountId")] pub enum AccountEvent { Asset(AssetEvent), Created(AccountId), @@ -146,26 +149,6 @@ mod account { MetadataInserted(AccountId), MetadataRemoved(AccountId), } - - impl Identifiable for AccountEvent { - type Id = AccountId; - - fn id(&self) -> &AccountId { - match self { - Self::Asset(asset) => &asset.id().account_id, - Self::Created(id) - | Self::Deleted(id) - | Self::AuthenticationAdded(id) - | Self::AuthenticationRemoved(id) - | Self::PermissionAdded(id) - | Self::PermissionRemoved(id) - | Self::RoleRevoked(id) - | Self::RoleGranted(id) - | Self::MetadataInserted(id) - | Self::MetadataRemoved(id) => id, - } - } - } } mod domain { @@ -175,8 +158,19 @@ mod domain { /// Domain Event #[derive( - Clone, PartialEq, Eq, Debug, Decode, Encode, Deserialize, Serialize, IntoSchema, Filter, + Clone, + PartialEq, + Eq, + Debug, + Decode, + Encode, + Deserialize, + Serialize, + IntoSchema, + Filter, + Identifiable, )] + #[id(type = "DomainId")] #[non_exhaustive] #[allow(missing_docs)] pub enum DomainEvent { @@ -187,21 +181,6 @@ mod domain { MetadataInserted(DomainId), MetadataRemoved(DomainId), } - - impl Identifiable for DomainEvent { - type Id = DomainId; - - fn id(&self) -> &DomainId { - match self { - Self::Account(account) => &account.id().domain_id, - Self::AssetDefinition(asset_definition) => &asset_definition.id().domain_id, - Self::Created(id) - | Self::Deleted(id) - | Self::MetadataInserted(id) - | Self::MetadataRemoved(id) => id, - } - } - } } mod trigger { @@ -211,8 +190,19 @@ mod trigger { /// Trigger Event #[derive( - Clone, PartialEq, Eq, Debug, Decode, Encode, Deserialize, Serialize, IntoSchema, Filter, + Clone, + PartialEq, + Eq, + Debug, + Decode, + Encode, + Deserialize, + Serialize, + IntoSchema, + Filter, + Identifiable, )] + #[id(type = "TriggerId")] #[non_exhaustive] #[allow(missing_docs)] pub enum TriggerEvent { @@ -221,19 +211,6 @@ mod trigger { Extended(TriggerId), Shortened(TriggerId), } - - impl Identifiable for TriggerEvent { - type Id = TriggerId; - - fn id(&self) -> &TriggerId { - match self { - Self::Created(id) - | Self::Deleted(id) - | Self::Extended(id) - | Self::Shortened(id) => id, - } - } - } } /// World event diff --git a/data_model/src/lib.rs b/data_model/src/lib.rs index 0665f3208aa..c90a113f173 100644 --- a/data_model/src/lib.rs +++ b/data_model/src/lib.rs @@ -21,6 +21,7 @@ use block_value::BlockValue; use derive_more::Display; use events::FilterBox; use iroha_crypto::{Hash, PublicKey}; +use iroha_data_model_derive::Identifiable; use iroha_data_primitives::small::SmallVec; pub use iroha_data_primitives::{self as primitives, fixed, small}; use iroha_macro::{error::ErrorTryFromEnum, FromVariant}; @@ -162,6 +163,7 @@ pub enum Parameter { Debug, Display, Clone, + Identifiable, PartialEq, Eq, PartialOrd, @@ -174,6 +176,7 @@ pub enum Parameter { IntoSchema, )] #[allow(clippy::enum_variant_names)] +#[id(type = "Self")] pub enum IdBox { /// [`DomainId`](`domain::Id`) variant. DomainId(::Id), @@ -191,13 +194,13 @@ pub enum IdBox { RoleId(::Id), } -impl Identifiable for IdBox { - type Id = Self; +// impl Identifiable for IdBox { +// type Id = Self; - fn id(&self) -> &Self::Id { - self - } -} +// fn id(&self) -> &Self::Id { +// self +// } +// } /// Sized container for constructors of all [`Identifiable`]s that can be registered via transaction #[derive( diff --git a/data_model/src/role.rs b/data_model/src/role.rs index 3e28695b5c8..d3378aaabba 100644 --- a/data_model/src/role.rs +++ b/data_model/src/role.rs @@ -2,6 +2,7 @@ #[cfg(not(feature = "std"))] use alloc::{collections::btree_set, format, string::String, vec::Vec}; +use iroha_data_model_derive::OrdEqHash; #[cfg(feature = "std")] use std::collections::btree_set; @@ -49,18 +50,19 @@ pub struct Id { Debug, Display, Clone, - PartialEq, - Eq, + OrdEqHash, Getters, Decode, Encode, Deserialize, Serialize, IntoSchema, + Identifiable, )] #[cfg_attr(feature = "ffi_api", ffi_bindgen)] #[display(fmt = "{id}")] #[getset(get = "pub")] +#[id(type = "Id")] pub struct Role { /// Unique name of the role. #[getset(skip)] @@ -70,20 +72,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_api", ffi_bindgen)] impl Role { /// Constructor. @@ -99,31 +87,13 @@ 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, OrdEqHash, Getters, Decode, Encode, Deserialize, Serialize, IntoSchema, )] pub struct NewRole { inner: Role, @@ -138,20 +108,6 @@ impl Identifiable 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) - } -} - /// Builder for [`Role`] impl NewRole { /// Constructor