diff --git a/data_model/derive/src/lib.rs b/data_model/derive/src/lib.rs index 030c317e383..813e27b6b0d 100644 --- a/data_model/derive/src/lib.rs +++ b/data_model/derive/src/lib.rs @@ -1,8 +1,61 @@ -//! 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` + +#![allow( + clippy::expect_used, + clippy::too_many_lines, + clippy::eval_order_dependence, + clippy::unwrap_in_result +)] 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, +}; + +/// A derive macro for `Identifiable` trait. Currently supports derivations only for +/// `IdBox`, `Event` enums, and structs from the `data_model` crate (with the exception of `NewRole` due +/// to its unconventional trait implementation). +/// +/// 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. +/// +/// Example: +/// ```rust +/// +/// // For a struct decorated like this +/// #[derive(Identifiable)] +/// #[id(type = "::Id")] +/// pub struct NewDomain { +/// /// The identification associated with the domain builder. +/// id: ::Id, +/// /// The (IPFS) link to the logo of this domain. +/// logo: Option, +/// /// Metadata associated with 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 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. @@ -16,214 +69,516 @@ 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 event = parse_macro_input!(input as filter::EventEnum); + filter::impl_filter(&event) +} + +/// 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_from_filter(&ast) + id::impl_ordeqhash(&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>) - }) +mod id { + use super::*; + + pub(super) 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, + } + 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 { + filter::EventVariant::IdField(_) => None, + filter::EventVariant::EventField { + variant: event_variant_ident, + .. + } => { + let inner_event_field_ident = format_ident!( + "{}_id", + id_type + .path + .get_ident() + .expect("Supplied `id_type` should be an ident convertible type path") + .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 { + filter::EventVariant::IdField(event_variant_ident) => Some(quote! { + Self::#event_variant_ident(id) + }), + filter::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 content; + Ok(IdInput { + _attrs: attrs, + _vis: vis, + _enum_token: None, + _struct_token: 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 { - None + let enum_token = Some(input.parse::()?); + let ident = input.parse::()?; + let content; + if ident.to_string().ends_with("Event") { + Ok(IdInput { + _attrs: attrs, + _vis: vis, + _enum_token: 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(filter::EventVariant::parse)?, + ), + id_type, + }) + } else { + // Box case + Ok(IdInput { + _attrs: attrs, + _vis: vis, + _enum_token: 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, + }) + } } - }) - .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) + } + } + + pub(super) fn impl_ordeqhash(ast: &syn::DeriveInput) -> TokenStream { + let name = ast.ident.clone(); + + quote! { + impl core::cmp::PartialOrd for #name { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) } } - }) - .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 (_)) + 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); + } } - }) - .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> } + .into() + } + + pub(super) 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; - 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, + 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; - /// Get `origin_filter` - #[inline] - pub const fn origin_filter(&self) -> &#import_path::FilterOpt<#import_path::OriginFilter<#import_path::#event_ident>> { - &self.origin_filter + fn id(&self) -> &Self::Id { + &self.id + } + } } + .into() + } else { + quote! { + impl Identifiable for #name { + type Id = #id; - /// Get `event_filter` - #[inline] - pub const fn event_filter(&self) -> &#import_path::FilterOpt<#event_filter_ident> { - &self.event_filter + fn id(&self) -> &Self::Id { + self + } + } } + .into() } + } - 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) - } + 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`") + } +} + +mod filter { + use super::*; + + pub(super) struct EventEnum { + _attrs: Vec, + vis: Visibility, + _enum_token: Token![enum], + ident: Ident, + generics: Generics, + _brace_token: Brace, + variants: Punctuated, + } + + pub(super) 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() } - #[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),* + 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() } - 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, - } + 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 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") { + Ok(event) + } else { + Err(syn::Error::new_spanned( + event.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, "Wrong type path")) + } + } } - .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 + pub(super) fn impl_filter(event: &EventEnum) -> 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 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 id_fil = quote! { #import_path::IdFilter }; + let imp_event = quote! { #import_path::#event_ident }; + + let filter_doc = format!("{}", filter_ident); + let new_doc = format!("{}", filter_ident); + + quote! { + #[derive( + Clone, + PartialEq, + PartialOrd, + Ord, + Eq, + Debug, + Decode, + Encode, + Deserialize, + Serialize, + IntoSchema, + Hash, + )] + #[doc = #filter_doc] + #vis struct #filter_ident #generics { + 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: #fil_opt<#id_fil<<#imp_event as Identifiable>::Id>>, + event_filter: #fil_opt<#event_filter_ident>, + ) -> Self { + Self { + id_filter, + event_filter, + } + } + + /// Get `id_filter` + #[inline] + 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) -> &#fil_opt<#event_filter_ident> { + &self.event_filter + } + } + + impl Filter for #filter_ident { + type EventType = #imp_event; + fn matches(&self, entity: &Self::EventType) -> bool { + self.id_filter.matches(entity.id()) && self.event_filter.matches(entity) + } + } + + #[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 { + #(#id_variants),*, + #(#event_variants),* + } + + impl 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, + } + } + } + + } + .into() } } diff --git a/data_model/src/account.rs b/data_model/src/account.rs index 93d1022b020..50516aae566 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; @@ -126,9 +127,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, @@ -210,8 +221,7 @@ impl NewAccount { Debug, Display, Clone, - PartialEq, - Eq, + OrdEqHash, Getters, MutGetters, Setters, @@ -220,10 +230,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, @@ -243,14 +255,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 @@ -261,20 +265,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 8fe7ba6850e..436c350f244 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, Setters}; +use iroha_data_model_derive::OrdEqHash; use iroha_macro::FromVariant; use iroha_primitives::{fixed, fixed::Fixed}; use iroha_schema::IntoSchema; @@ -113,8 +114,7 @@ impl AssetDefinitionEntry { Debug, Display, Clone, - PartialEq, - Eq, + OrdEqHash, Getters, MutGetters, Setters, @@ -123,10 +123,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, @@ -147,20 +149,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. @@ -198,18 +186,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)] @@ -299,20 +288,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 { @@ -407,9 +382,19 @@ 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, + OrdEqHash, + Decode, + Encode, + Deserialize, + Serialize, + IntoSchema, + Identifiable, )] #[display(fmt = "{id} {mintable}{value_type}")] +#[id(type = "::Id")] pub struct NewAssetDefinition { id: ::Id, value_type: AssetValueType, @@ -439,20 +424,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 { @@ -601,26 +572,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 091f0cdd423..66d775d908a 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_primitives::conststr::ConstString; @@ -72,16 +73,26 @@ 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. + /// 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, } @@ -109,20 +120,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] @@ -162,8 +159,7 @@ impl NewDomain { Debug, Display, Clone, - PartialEq, - Eq, + OrdEqHash, Getters, MutGetters, Decode, @@ -171,10 +167,12 @@ impl 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, @@ -197,32 +195,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 44311915c7e..008cf4d4d2c 100644 --- a/data_model/src/events/data/events.rs +++ b/data_model/src/events/data/events.rs @@ -27,6 +27,7 @@ mod asset { )] #[non_exhaustive] #[allow(missing_docs)] + #[id(type = "AssetId")] pub enum AssetEvent { Created(AssetId), Deleted(AssetId), @@ -68,6 +69,7 @@ mod asset { )] #[non_exhaustive] #[allow(missing_docs)] + #[id(type = "AssetDefinitionId")] pub enum AssetDefinitionEvent { Created(AssetDefinitionId), MintabilityChanged(AssetDefinitionId), @@ -116,6 +118,7 @@ mod peer { )] #[non_exhaustive] #[allow(missing_docs)] + #[id(type = "PeerId")] pub enum PeerEvent { Added(PeerId), Removed(PeerId), @@ -154,6 +157,7 @@ mod role { )] #[non_exhaustive] #[allow(missing_docs)] + #[id(type = "RoleId")] pub enum RoleEvent { Created(RoleId), Deleted(RoleId), @@ -193,6 +197,7 @@ mod account { )] #[non_exhaustive] #[allow(missing_docs)] + #[id(type = "AccountId")] pub enum AccountEvent { Asset(AssetEvent), Created(AccountId), @@ -249,6 +254,7 @@ mod domain { IntoSchema, Filter, )] + #[id(type = "DomainId")] #[non_exhaustive] #[allow(missing_docs)] pub enum DomainEvent { @@ -297,6 +303,7 @@ mod trigger { IntoSchema, Filter, )] + #[id(type = "TriggerId")] #[non_exhaustive] #[allow(missing_docs)] pub enum TriggerEvent { diff --git a/data_model/src/lib.rs b/data_model/src/lib.rs index 74a1e64b97b..fb6c762a8fa 100644 --- a/data_model/src/lib.rs +++ b/data_model/src/lib.rs @@ -163,6 +163,7 @@ pub enum Parameter { Debug, Display, Clone, + Identifiable, PartialEq, Eq, PartialOrd, @@ -175,6 +176,7 @@ pub enum Parameter { IntoSchema, )] #[allow(clippy::enum_variant_names)] +#[id(type = "Self")] pub enum IdBox { /// [`DomainId`](`domain::Id`) variant. DomainId(::Id), diff --git a/data_model/src/peer.rs b/data_model/src/peer.rs index ccfc751c97d..e08cfe12ed5 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::OrdEqHash; use iroha_schema::IntoSchema; use parity_scale_codec::{Decode, Encode}; use serde::{Deserialize, Serialize}; @@ -15,20 +16,7 @@ use serde::{Deserialize, Serialize}; use crate::{Identifiable, PublicKey, Registered, Value}; /// Peer represents Iroha instance. -#[derive( - Debug, - Display, - Clone, - PartialEq, - Eq, - PartialOrd, - Ord, - Decode, - Encode, - Deserialize, - Serialize, - IntoSchema, -)] +#[derive(Debug, Display, Clone, OrdEqHash, Decode, Encode, Deserialize, Serialize, IntoSchema)] #[display(fmt = "@@{}", "id.address")] pub struct Peer { /// Peer Identification. diff --git a/data_model/src/role.rs b/data_model/src/role.rs index cc669982c42..88306df6f96 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::OrdEqHash; #[cfg(feature = "ffi_api")] use iroha_ffi::ffi_bindgen; use iroha_schema::IntoSchema; @@ -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, @@ -140,20 +110,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) - } -} - /// Builder for [`Role`] impl NewRole { /// Constructor