From 3dbcb76310aeaafb2f85da2bbb3f5063e27defb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marin=20Ver=C5=A1i=C4=87?= Date: Tue, 11 Jan 2022 16:22:50 +0100 Subject: [PATCH] fix schema generation for HashOf, SignatureOf, add test to ensure no schemas are missing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marin Veršić --- crypto/src/hash.rs | 18 ++++- crypto/src/signature.rs | 18 ++++- data_model/src/expression.rs | 4 +- data_model/src/isi.rs | 7 -- schema/bin/src/main.rs | 144 ++++++++++++++++++----------------- schema/src/lib.rs | 36 +++++---- 6 files changed, 129 insertions(+), 98 deletions(-) diff --git a/crypto/src/hash.rs b/crypto/src/hash.rs index c774cc940f8..2f1999e9c83 100644 --- a/crypto/src/hash.rs +++ b/crypto/src/hash.rs @@ -7,7 +7,7 @@ use core::{ }; use derive_more::{Deref, DerefMut, Display}; -use iroha_schema::IntoSchema; +use iroha_schema::prelude::*; use parity_scale_codec::{Decode, Encode}; use serde::{Deserialize, Serialize}; #[cfg(feature = "std")] @@ -157,8 +157,20 @@ impl HashOf { } impl IntoSchema for HashOf { - fn schema(metamap: &mut iroha_schema::MetaMap) { - Hash::schema(metamap) + fn schema(map: &mut MetaMap) { + let type_name = Self::type_name(); + + Hash::schema(map); + if !map.contains_key(&type_name) { + // Field was just inserted above + #[allow(clippy::expect_used)] + let wrapped_type_metadata = map + .get(&Hash::type_name()) + .expect("Wrapped type metadata should have been present in the schemas") + .clone(); + + map.insert(type_name, wrapped_type_metadata); + } } } diff --git a/crypto/src/signature.rs b/crypto/src/signature.rs index ccf4bc6a4d2..456294e8bd7 100644 --- a/crypto/src/signature.rs +++ b/crypto/src/signature.rs @@ -11,7 +11,7 @@ use core::{fmt, marker::PhantomData}; use std::collections::{btree_map, btree_set}; use derive_more::{Deref, DerefMut}; -use iroha_schema::IntoSchema; +use iroha_schema::prelude::*; use parity_scale_codec::{Decode, Encode}; use serde::{Deserialize, Serialize}; #[cfg(feature = "std")] @@ -150,8 +150,20 @@ impl Ord for SignatureOf { } impl IntoSchema for SignatureOf { - fn schema(metamap: &mut iroha_schema::MetaMap) { - Signature::schema(metamap) + fn schema(map: &mut MetaMap) { + let type_name = Self::type_name(); + + Signature::schema(map); + if !map.contains_key(&type_name) { + // Field was just inserted above + #[allow(clippy::expect_used)] + let wrapped_type_metadata = map + .get(&Signature::type_name()) + .expect("Wrapped type metadata should have been present in the schemas") + .clone(); + + map.insert(type_name, wrapped_type_metadata); + } } } diff --git a/data_model/src/expression.rs b/data_model/src/expression.rs index 465a67ea3c8..f2d419880af 100644 --- a/data_model/src/expression.rs +++ b/data_model/src/expression.rs @@ -1,7 +1,7 @@ //! Expressions to use inside of ISIs. #![allow( - // Because of `serde(skip)` + // Because of `codec(skip)` clippy::default_trait_access, // Because of length on instructions and expressions (can't be 0) clippy::len_without_is_empty, @@ -33,11 +33,11 @@ pub type ExpressionBox = Box; /// Struct for type checking and converting expression results. #[derive(Debug, Clone, Encode, Decode, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] +#[serde(transparent)] pub struct EvaluatesTo> { /// Expression. #[serde(flatten)] pub expression: ExpressionBox, - #[serde(skip)] #[codec(skip)] _value_type: PhantomData, } diff --git a/data_model/src/isi.rs b/data_model/src/isi.rs index 1a4a598bef5..1545570311a 100644 --- a/data_model/src/isi.rs +++ b/data_model/src/isi.rs @@ -84,13 +84,6 @@ pub struct RemoveKeyValueBox { pub key: EvaluatesTo, } -/// Sized structure for all possible Sets. -#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, PartialEq, Eq, IntoSchema)] -pub struct SetBox { - /// Object to set as a value. - pub object: EvaluatesTo, -} - /// Sized structure for all possible Registers. #[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, PartialEq, Eq, IntoSchema)] pub struct RegisterBox { diff --git a/schema/bin/src/main.rs b/schema/bin/src/main.rs index 228b548ba6c..b3691499e4e 100644 --- a/schema/bin/src/main.rs +++ b/schema/bin/src/main.rs @@ -2,78 +2,23 @@ #![allow(clippy::print_stdout)] -use std::collections::BTreeMap; - use iroha_core::block::stream::prelude::*; use iroha_schema::prelude::*; -macro_rules! to_json { - ($($t:ty),* $(,)?) => {{ - let mut out = BTreeMap::new(); - $(<$t as IntoSchema>::schema(&mut out);)* - serde_json::to_string_pretty(&out).unwrap() - }}; -} - -fn main() { +fn build_schemas() -> MetaMap { use iroha_core::genesis::RawGenesisBlock; - use iroha_data_model::{ - expression::*, - isi::{If, *}, - prelude::*, - }; + use iroha_data_model::prelude::*; - let json = to_json! { - // $ rg '^pub (struct|enum)' | rg -v '(<|Builder|LengthLimits|QueryRequest)' | cut -d' ' -f3 | sed -e 's/[(].*//' -e 's/$/,/' | sort - Add, - And, - BlockPublisherMessage, - BlockSubscriberMessage, - BurnBox, - Contains, - ContainsAll, - ContainsAny, - ContextValue, - Divide, - Equal, - Event, - EventFilter, - EventPublisherMessage, - EventSubscriberMessage, - Expression, - FailBox, - GrantBox, - Greater, - IdBox, - IdentifiableBox, - If, - If, - Instruction, - Less, - MintBox, - Mod, - Multiply, - Not, - Or, - Pair, - Parameter, - Payload, - QueryBox, - QueryResult, - RaiseTo, - RegisterBox, - RemoveKeyValueBox, - SequenceBox, - SetBox, - SetKeyValueBox, - SignedQueryRequest, - Subtract, - TransferBox, - UnregisterBox, - Value, - Where, + macro_rules! schemas { + ($($t:ty),* $(,)?) => {{ + let mut out = MetaMap::new(); + $(<$t as IntoSchema>::schema(&mut out);)* + out + }}; + } - // All versioned + schemas! { + // It is sufficient to list top level types only VersionedBlockPublisherMessage, VersionedBlockSubscriberMessage, VersionedEventPublisherMessage, @@ -83,7 +28,70 @@ fn main() { VersionedTransaction, RawGenesisBlock - }; + } +} + +// Schemas should always be serializable to JSON +#[allow(clippy::expect_used)] +fn main() { + let schemas = build_schemas(); + + println!( + "{}", + serde_json::to_string_pretty(&schemas).expect("Unable to serialize schemas") + ); +} + +#[cfg(test)] +mod tests { + use std::collections::HashMap; + + use super::*; + + fn find_missing_schemas(schemas: &MetaMap) -> HashMap<&str, Vec<&str>> { + let mut missing_schemas = HashMap::new(); + + for (type_name, schema) in schemas { + let types: Vec<&str> = match schema { + Metadata::Enum(EnumMeta { variants }) => variants + .iter() + .map(|v| &v.ty) + .filter_map(Option::as_ref) + .map(String::as_str) + .collect(), + Metadata::Struct(NamedFieldsMeta { declarations }) => { + declarations.iter().map(|d| d.ty.as_str()).collect() + } + Metadata::TupleStruct(UnnamedFieldsMeta { types }) => { + types.iter().map(String::as_str).collect() + } + Metadata::Result(ResultMeta { ok, err }) => vec![ok, err], + Metadata::Map(MapMeta { key, value }) => vec![key, value], + Metadata::Option(ty) + | Metadata::Array(ArrayMeta { ty, .. }) + | Metadata::Vec(ty) => { + vec![ty] + } + Metadata::String | Metadata::Bool | Metadata::FixedPoint(_) | Metadata::Int(_) => { + vec![] + } + }; + + for ty in types { + if !schemas.contains_key(ty) { + missing_schemas + .entry(type_name.as_str()) + .or_insert_with(Vec::new) + .push(ty); + } + } + } + + missing_schemas + } - println!("{}", json) + #[test] + fn no_missing_schemas() { + assert!(find_missing_schemas(&build_schemas()).is_empty()); + } } diff --git a/schema/src/lib.rs b/schema/src/lib.rs index 4ae93188f25..f5376c05e36 100644 --- a/schema/src/lib.rs +++ b/schema/src/lib.rs @@ -51,7 +51,7 @@ pub trait DecimalPlacesAware { } /// Metadata -#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Clone)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] pub enum Metadata { /// Structure with named fields Struct(NamedFieldsMeta), @@ -80,14 +80,16 @@ pub enum Metadata { } /// Array metadata -#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Clone)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] pub struct ArrayMeta { - ty: String, - len: usize, + /// Type + pub ty: String, + /// Length + pub len: usize, } /// Named fields -#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Clone)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] pub struct NamedFieldsMeta { /// Fields pub declarations: Vec, @@ -95,7 +97,7 @@ pub struct NamedFieldsMeta { } /// Field -#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Clone)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] pub struct Declaration { /// Field name pub name: String, @@ -104,7 +106,7 @@ pub struct Declaration { } /// Unnamed fileds -#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Clone)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] pub struct UnnamedFieldsMeta { /// Field types pub types: Vec, @@ -112,14 +114,14 @@ pub struct UnnamedFieldsMeta { } /// Enum metadata -#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Clone)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] pub struct EnumMeta { /// Enum variants pub variants: Vec, } /// Enum variant -#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Clone)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] pub struct EnumVariant { /// Enum variant name pub name: String, @@ -131,7 +133,7 @@ pub struct EnumVariant { } /// Result variant -#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Clone)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] pub struct ResultMeta { /// Ok type pub ok: String, @@ -140,7 +142,7 @@ pub struct ResultMeta { } /// Map variant -#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Clone)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] pub struct MapMeta { /// Key type pub key: String, @@ -149,7 +151,7 @@ pub struct MapMeta { } /// Integer mode -#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] pub enum IntMode { /// Fixed width FixedWidth, @@ -158,11 +160,11 @@ pub enum IntMode { } /// Compact predicate. Just for documentation purposes -#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] pub struct Compact(T); /// Fixed metadata -#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Clone)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] pub struct FixedMeta { base: String, decimal_places: usize, @@ -319,7 +321,11 @@ impl IntoSchema for BTreeSet { format!("BTreeSet<{}>", V::type_name()) } fn schema(map: &mut MetaMap) { - Vec::::schema(map) + map.entry(Self::type_name()) + .or_insert_with(|| Metadata::Vec(V::type_name())); + if !map.contains_key(&V::type_name()) { + Vec::::schema(map) + } } }