From 37c0a577ccd9973ee246b37a558f1b3b2c225391 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 7 Dec 2020 11:48:59 +0000 Subject: [PATCH 01/28] Parameterize CompactForm String for optional SCALE impl By default, parity-scale-codec does not provide Encode/Decode impls for an owned String. This is only provided under the "full" feature which is not used by the substrate runtime, because it should not be used for consensus critical code. So in order for the CompactForm to be integrated into the substrate runtime, or wherever the "full" feature cannot be used, then we must parameterize the `String` type so that it can be both an `&'static str` on the runtime side where it is encoded, and a `String` in client/consuming code where it is decoded. --- src/form.rs | 13 ++++++++----- src/registry.rs | 22 +++++++++++++++------- test_suite/tests/codec.rs | 5 +++-- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/form.rs b/src/form.rs index f2a56ac4..f323123f 100644 --- a/src/form.rs +++ b/src/form.rs @@ -33,7 +33,7 @@ use crate::prelude::{ any::TypeId, fmt::Debug, - string::String, + marker::PhantomData, }; use crate::{ @@ -51,7 +51,7 @@ pub trait Form { /// The type representing the type. type Type: PartialEq + Eq + PartialOrd + Ord + Clone + Debug; /// The string type. - type String: Serialize + PartialEq + Eq + PartialOrd + Ord + Clone + Debug; + type String: PartialEq + Eq + PartialOrd + Ord + Clone + Debug; } /// A meta meta-type. @@ -76,9 +76,12 @@ impl Form for MetaForm { /// /// `type String` is owned in order to enable decoding #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Serialize, Debug)] -pub enum CompactForm {} +pub struct CompactForm(PhantomData); -impl Form for CompactForm { +impl Form for CompactForm +where + S: PartialEq + Eq + PartialOrd + Ord + Clone + Debug, +{ type Type = UntrackedSymbol; - type String = String; + type String = S; } diff --git a/src/registry.rs b/src/registry.rs index 30cc9d24..425df457 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -27,9 +27,9 @@ use crate::prelude::{ any::TypeId, collections::BTreeMap, + fmt::Debug, mem, num::NonZeroU32, - string::ToString, vec::Vec, }; @@ -50,6 +50,7 @@ use scale::{ Encode, }; use serde::{ + de::DeserializeOwned, Deserialize, Serialize, }; @@ -67,7 +68,7 @@ impl IntoCompact for &'static str { type Output = ::String; fn into_compact(self, _registry: &mut Registry) -> Self::Output { - self.to_string() + self } } @@ -201,8 +202,12 @@ impl Registry { /// A read-only registry, to be used for decoding/deserializing #[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Decode)] -pub struct RegistryReadOnly { - types: Vec>, +#[serde(bound(serialize = "S: Serialize", deserialize = "S: DeserializeOwned",))] +pub struct RegistryReadOnly +where + S: PartialEq + Eq + PartialOrd + Ord + Clone + Debug, +{ + types: Vec>>, } impl From for RegistryReadOnly { @@ -213,14 +218,17 @@ impl From for RegistryReadOnly { } } -impl RegistryReadOnly { +impl RegistryReadOnly +where + S: PartialEq + Eq + PartialOrd + Ord + Clone + Debug, +{ /// Returns the type definition for the given identifier, `None` if no type found for that ID. - pub fn resolve(&self, id: NonZeroU32) -> Option<&Type> { + pub fn resolve(&self, id: NonZeroU32) -> Option<&Type>> { self.types.get((id.get() - 1) as usize) } /// Returns an iterator for all types paired with their associated NonZeroU32 identifier. - pub fn enumerate(&self) -> impl Iterator)> { + pub fn enumerate(&self) -> impl Iterator>)> { self.types.iter().enumerate().map(|(i, ty)| { let id = NonZeroU32::new(i as u32 + 1).expect("i + 1 > 0; qed"); (id, ty) diff --git a/test_suite/tests/codec.rs b/test_suite/tests/codec.rs index c6ef0461..6d2f398e 100644 --- a/test_suite/tests/codec.rs +++ b/test_suite/tests/codec.rs @@ -28,6 +28,7 @@ use scale_info::{ form::CompactForm, prelude::{ num::NonZeroU32, + string::String, vec, vec::Vec, }, @@ -60,7 +61,7 @@ fn scale_encode_then_decode_to_readonly() { let mut encoded = registry.encode(); let original_serialized = serde_json::to_value(registry).unwrap(); - let readonly_decoded = RegistryReadOnly::decode(&mut &encoded[..]).unwrap(); + let readonly_decoded = RegistryReadOnly::::decode(&mut &encoded[..]).unwrap(); assert!(readonly_decoded .resolve(NonZeroU32::new(1).unwrap()) .is_some()); @@ -76,7 +77,7 @@ fn json_serialize_then_deserialize_to_readonly() { let original_serialized = serde_json::to_value(registry).unwrap(); // assert_eq!(original_serialized, serde_json::Value::Null); - let readonly_deserialized: RegistryReadOnly = + let readonly_deserialized: RegistryReadOnly = serde_json::from_value(original_serialized.clone()).unwrap(); assert!(readonly_deserialized .resolve(NonZeroU32::new(1).unwrap()) From 9a7ccbf5dec5abc6d85244ea7eb1adeec0a8e1f6 Mon Sep 17 00:00:00 2001 From: David Palm Date: Mon, 14 Dec 2020 12:44:07 +0100 Subject: [PATCH 02/28] Fix no-std compilation --- src/form.rs | 3 ++- src/registry.rs | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/form.rs b/src/form.rs index 30537ba2..ae8a0db2 100644 --- a/src/form.rs +++ b/src/form.rs @@ -78,7 +78,8 @@ impl Form for MetaForm { /// underlying data. /// /// `type String` is owned in order to enable decoding -#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Serialize, Debug)] +#[cfg_attr(feature = "serde", derive(Serialize))] +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)] pub struct CompactForm(PhantomData); impl Form for CompactForm diff --git a/src/registry.rs b/src/registry.rs index 4945e0dc..7fc0b42a 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -204,8 +204,9 @@ impl Registry { } /// A read-only registry, to be used for decoding/deserializing -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Decode)] -#[serde(bound(serialize = "S: Serialize", deserialize = "S: DeserializeOwned",))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, PartialEq, Eq, Decode)] +#[cfg_attr(feature = "serde", serde(bound(serialize = "S: Serialize", deserialize = "S: DeserializeOwned")))] pub struct RegistryReadOnly where S: PartialEq + Eq + PartialOrd + Ord + Clone + Debug, From 254fee129b5a66b7a61998654b5f04ece22c4ec7 Mon Sep 17 00:00:00 2001 From: David Palm Date: Mon, 14 Dec 2020 12:47:33 +0100 Subject: [PATCH 03/28] Obey the fmt --- src/registry.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/registry.rs b/src/registry.rs index 7fc0b42a..446c199c 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -206,7 +206,10 @@ impl Registry { /// A read-only registry, to be used for decoding/deserializing #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, PartialEq, Eq, Decode)] -#[cfg_attr(feature = "serde", serde(bound(serialize = "S: Serialize", deserialize = "S: DeserializeOwned")))] +#[cfg_attr( + feature = "serde", + serde(bound(serialize = "S: Serialize", deserialize = "S: DeserializeOwned")) +)] pub struct RegistryReadOnly where S: PartialEq + Eq + PartialOrd + Ord + Clone + Debug, From e74e4f9eb3e11b3895bec5d17327f1eed84de546 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 16 Dec 2020 09:50:25 +0000 Subject: [PATCH 04/28] Introduce String trait for Form --- src/form.rs | 14 ++++++++++++-- src/registry.rs | 5 +++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/form.rs b/src/form.rs index ae8a0db2..2e21cd37 100644 --- a/src/form.rs +++ b/src/form.rs @@ -34,6 +34,7 @@ use crate::prelude::{ any::TypeId, fmt::Debug, marker::PhantomData, + string::String, }; use crate::{ @@ -53,9 +54,18 @@ pub trait Form { /// The type representing the type. type Type: PartialEq + Eq + PartialOrd + Ord + Clone + Debug; /// The string type. - type String: PartialEq + Eq + PartialOrd + Ord + Clone + Debug; + type String: FormString; } +/// Trait for types which can be used to represent strings in type definitions. +pub trait FormString: + AsRef + PartialEq + Eq + PartialOrd + Ord + Clone + Debug +{ +} + +impl FormString for &'static str {} +impl FormString for String {} + /// A meta meta-type. /// /// Allows to be converted into other forms such as compact form @@ -84,7 +94,7 @@ pub struct CompactForm(PhantomData); impl Form for CompactForm where - S: PartialEq + Eq + PartialOrd + Ord + Clone + Debug, + S: FormString, { type Type = UntrackedSymbol; type String = S; diff --git a/src/registry.rs b/src/registry.rs index 446c199c..016f8719 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -37,6 +37,7 @@ use crate::{ form::{ CompactForm, Form, + FormString, }, interner::{ Interner, @@ -212,7 +213,7 @@ impl Registry { )] pub struct RegistryReadOnly where - S: PartialEq + Eq + PartialOrd + Ord + Clone + Debug, + S: FormString, { types: Vec>>, } @@ -227,7 +228,7 @@ impl From for RegistryReadOnly { impl RegistryReadOnly where - S: PartialEq + Eq + PartialOrd + Ord + Clone + Debug, + S: FormString, { /// Returns the type definition for the given identifier, `None` if no type found for that ID. pub fn resolve(&self, id: NonZeroU32) -> Option<&Type>> { From 7860c791ea2befb79a451216b2cdf12fa148711c Mon Sep 17 00:00:00 2001 From: David Palm Date: Wed, 16 Dec 2020 11:22:20 +0100 Subject: [PATCH 05/28] Rename "Compact" to "Frozen" (and associated fallout) Add a `compact` member to `Field` to indicate it is `scale_codec::Compact` --- src/form.rs | 6 +- src/lib.rs | 45 ++++++----- src/registry.rs | 38 ++++----- src/tests.rs | 25 ++++++ src/ty/composite.rs | 12 +-- src/ty/fields.rs | 39 +++++++--- src/ty/mod.rs | 49 ++++++------ src/ty/path.rs | 16 ++-- src/ty/variant.rs | 22 +++--- test_suite/tests/codec.rs | 4 +- test_suite/tests/derive.rs | 156 ++++++++++++++++++++++++++++++++++++- test_suite/tests/json.rs | 6 +- 12 files changed, 309 insertions(+), 109 deletions(-) diff --git a/src/form.rs b/src/form.rs index ae8a0db2..c9302f99 100644 --- a/src/form.rs +++ b/src/form.rs @@ -22,7 +22,7 @@ //! It uses `MetaType` for communicating type identifiers and thus acts as //! a bridge from runtime to compile time type information. //! -//! The compact form is `CompactForm` and represents a compact form +//! The compact form is `FrozenForm` and represents a compact form //! that no longer has any connections to the interning registry and thus //! can no longer be used in order to retrieve information from the //! original registry easily. Its sole purpose is for compact serialization. @@ -80,9 +80,9 @@ impl Form for MetaForm { /// `type String` is owned in order to enable decoding #[cfg_attr(feature = "serde", derive(Serialize))] #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)] -pub struct CompactForm(PhantomData); +pub struct FrozenForm(PhantomData); -impl Form for CompactForm +impl Form for FrozenForm where S: PartialEq + Eq + PartialOrd + Ord + Clone + Debug, { diff --git a/src/lib.rs b/src/lib.rs index 4446005c..fb86ed29 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,49 +15,52 @@ #![cfg_attr(not(feature = "std"), no_std)] #![deny(missing_docs)] -//! Efficient and compact serialization of Rust types. +//! Efficient and space-efficient serialization of Rust types. //! //! This library provides structures to easily retrieve compile-time type -//! information at runtime and also to serialize this information in a compact -//! form. +//! information at runtime and also to serialize this information in a +//! space-efficient form, aka `FrozenForm`. //! //! # Registry //! -//! At the heart of its functionality is the [`Registry`](`crate::Registry`) that acts as cache for -//! known types in order to efficiently deduplicate them and thus compactify the overall -//! serialization. +//! At the heart of its functionality is the [`Registry`](`crate::Registry`) +//! that acts as a cache for known types in order to efficiently deduplicate +//! types and ensure a space-efficient serialization. //! //! # Type Information //! //! Information about types is provided via the [`TypeInfo`](`crate::TypeInfo`) trait. //! //! This trait should be implemented for all types that are serializable. -//! For this the library provides implementations for all commonly used Rust -//! standard types and provides derive macros for simpler implementation of user -//! provided custom types. +//! `scale-info` provides implementations for all commonly used Rust standard +//! types and a derive macro for implementing of custom types. //! //! # Compaction Forms //! -//! There is an uncompact form, called [`MetaForm`](`crate::form::MetaForm`) that acts as a bridge -//! from compile-time type information at runtime in order to easily retrieve all information needed +//! There is an expanded form, called [`MetaForm`](`crate::form::MetaForm`) that acts as a bridge +//! between compile-time type information and runtime, in order to easily retrieve all information needed //! to uniquely identify types. //! -//! The compact form is retrieved by the [`IntoCompact`](`crate::IntoCompact`) trait and internally -//! used by the [`Registry`](`crate::Registry`) in order to convert the uncompact types -//! into their compact form. +//! The `MetaForm` and its associated `Registry` can be transformed into the +//! space-efficient and universal form by the +//! [`IntoFrozen`](`crate::IntoFrozen`) trait and internally used by the +//! [`Registry`](`crate::Registry`) in order to convert the expanded types into +//! their space-efficient form. //! //! # Symbols and Namespaces //! -//! To differentiate two types sharing the same name namespaces are used. +//! To differentiate two types sharing the same name, namespaces are used. //! Commonly the namespace is equal to the one where the type has been defined in. For Rust prelude //! types such as [`Option`](`std::option::Option`) and [`Result`](`std::result::Result`) the root //! namespace (empty namespace) is used. //! -//! To use this library simply use the [`MetaForm`](`crate::form::MetaForm`) initially with your own data -//! structures and at best make them generic over the [`Form`](`crate::form::Form`) trait just as has -//! been done in this crate with [`TypeInfo`](`crate::TypeInfo`) in order to go for a simple -//! implementation of [`IntoCompact`](`crate::IntoCompact`). Use a single instance of the [`Registry`](`crate::Registry`) for -//! compaction and provide this registry instance upon serialization. Done. +//! To use this library simply use the [`MetaForm`](`crate::form::MetaForm`) +//! initially with your own data structures; make them generic over the +//! [`Form`](`crate::form::Form`) trait just as has been done in this crate with +//! [`TypeInfo`](`crate::TypeInfo`) in order to get a simple implementation of +//! [`IntoFrozen`](`crate::IntoFrozen`). +//! Use a single instance of the [`Registry`](`crate::Registry`) for compaction +//! and provide this registry instance upon serialization. Done. //! //! A usage example can be found in ink! here: //! https://github.com/paritytech/ink/blob/master/abi/src/specs.rs @@ -115,7 +118,7 @@ mod tests; pub use self::{ meta_type::MetaType, registry::{ - IntoCompact, + IntoFrozen, Registry, RegistryReadOnly, }, diff --git a/src/registry.rs b/src/registry.rs index 446c199c..82e63c5f 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -35,7 +35,7 @@ use crate::prelude::{ use crate::{ form::{ - CompactForm, + FrozenForm, Form, }, interner::{ @@ -56,19 +56,19 @@ use serde::{ Serialize, }; -/// Compacts the implementor using a registry. -pub trait IntoCompact { - /// The compact version of `Self`. +/// Freezes the type definition using a registry. +pub trait IntoFrozen { + /// The frozen version of `Self`. type Output; - /// Compacts `self` by using the registry for caching and compaction. - fn into_compact(self, registry: &mut Registry) -> Self::Output; + /// "Freezes" `self` by using the registry for caching and compaction. + fn into_frozen(self, registry: &mut Registry) -> Self::Output; } -impl IntoCompact for &'static str { - type Output = ::String; +impl IntoFrozen for &'static str { + type Output = ::String; - fn into_compact(self, _registry: &mut Registry) -> Self::Output { + fn into_frozen(self, _registry: &mut Registry) -> Self::Output { self } } @@ -98,14 +98,14 @@ pub struct Registry { /// /// This is going to be serialized upon serlialization. #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_registry_types"))] - types: BTreeMap, Type>, + types: BTreeMap, Type>, } /// Serializes the types of the registry by removing their unique IDs /// and instead serialize them in order of their removed unique ID. #[cfg(feature = "serde")] fn serialize_registry_types( - types: &BTreeMap, Type>, + types: &BTreeMap, Type>, serializer: S, ) -> Result where @@ -123,7 +123,7 @@ impl Default for Registry { impl Encode for Registry { fn size_hint(&self) -> usize { - mem::size_of::() + mem::size_of::>() * self.types.len() + mem::size_of::() + mem::size_of::>() * self.types.len() } fn encode_to(&self, dest: &mut W) { @@ -174,7 +174,7 @@ impl Registry { pub fn register_type(&mut self, ty: &MetaType) -> UntrackedSymbol { let (inserted, symbol) = self.intern_type_id(ty.type_id()); if inserted { - let compact_id = ty.type_info().into_compact(self); + let compact_id = ty.type_info().into_frozen(self); self.types.insert(symbol, compact_id); } symbol @@ -192,13 +192,13 @@ impl Registry { /// Converts an iterator into a Vec of the equivalent compact /// representations - pub fn map_into_compact(&mut self, iter: I) -> Vec + pub fn map_into_frozen(&mut self, iter: I) -> Vec where I: IntoIterator, - T: IntoCompact, + T: IntoFrozen, { iter.into_iter() - .map(|i| i.into_compact(self)) + .map(|i| i.into_frozen(self)) .collect::>() } } @@ -214,7 +214,7 @@ pub struct RegistryReadOnly where S: PartialEq + Eq + PartialOrd + Ord + Clone + Debug, { - types: Vec>>, + types: Vec>>, } impl From for RegistryReadOnly { @@ -230,12 +230,12 @@ where S: PartialEq + Eq + PartialOrd + Ord + Clone + Debug, { /// Returns the type definition for the given identifier, `None` if no type found for that ID. - pub fn resolve(&self, id: NonZeroU32) -> Option<&Type>> { + pub fn resolve(&self, id: NonZeroU32) -> Option<&Type>> { self.types.get((id.get() - 1) as usize) } /// Returns an iterator for all types paired with their associated NonZeroU32 identifier. - pub fn enumerate(&self) -> impl Iterator>)> { + pub fn enumerate(&self) -> impl Iterator>)> { self.types.iter().enumerate().map(|(i, ty)| { let id = NonZeroU32::new(i as u32 + 1).expect("i + 1 > 0; qed"); (id, ty) diff --git a/src/tests.rs b/src/tests.rs index 1552490c..73671ae2 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -154,3 +154,28 @@ fn struct_with_generics() { .composite(Fields::named().field_of::>>("data", "T")); assert_type!(SelfTyped, expected_type); } + +#[test] +fn struct_with_compact() { + use scale::Encode; + #[allow(unused)] + #[derive(Encode)] + struct Thicc { + #[codec(compact)] + stuff: u8 + } + + impl TypeInfo for Thicc { + type Identity = Self; + + fn type_info() -> Type { + Type::builder() + .path(Path::new("Thicc", module_path!())) + .composite(Fields::named().field_of::("stuff", "u8")) + .into() + } + } + + fn assert_type_info() {} + assert_type_info::(); +} diff --git a/src/ty/composite.rs b/src/ty/composite.rs index 6571bd38..2ec039d8 100644 --- a/src/ty/composite.rs +++ b/src/ty/composite.rs @@ -16,12 +16,12 @@ use crate::prelude::vec::Vec; use crate::{ form::{ - CompactForm, + FrozenForm, Form, MetaForm, }, Field, - IntoCompact, + IntoFrozen, Registry, }; use derive_more::From; @@ -81,12 +81,12 @@ pub struct TypeDefComposite { fields: Vec>, } -impl IntoCompact for TypeDefComposite { - type Output = TypeDefComposite; +impl IntoFrozen for TypeDefComposite { + type Output = TypeDefComposite; - fn into_compact(self, registry: &mut Registry) -> Self::Output { + fn into_frozen(self, registry: &mut Registry) -> Self::Output { TypeDefComposite { - fields: registry.map_into_compact(self.fields), + fields: registry.map_into_frozen(self.fields), } } } diff --git a/src/ty/fields.rs b/src/ty/fields.rs index 0b5755a3..e2bea876 100644 --- a/src/ty/fields.rs +++ b/src/ty/fields.rs @@ -14,11 +14,11 @@ use crate::{ form::{ - CompactForm, + FrozenForm, Form, MetaForm, }, - IntoCompact, + IntoFrozen, MetaType, Registry, TypeInfo, @@ -34,11 +34,12 @@ use serde::{ Serialize, }; -/// A field of a struct like data type. +/// A field of a struct-like data type. /// /// Name is optional so it can represent both named and unnamed fields. /// -/// This can be a named field of a struct type or an enum struct variant. +/// This can be a named field of a struct type or an enum struct variant, or an +/// unnamed field of a tuple struct. /// /// # Type name /// @@ -60,8 +61,9 @@ use serde::{ /// aliases. /// /// This is intended for informational and diagnostic purposes only. Although it -/// is possible to infer certain properties e.g. whether a type name is a type alias, -/// there are no guarantees provided, and the type name representation may change. +/// is possible to infer certain properties e.g. whether a type name is a type +/// alias, there are no guarantees provided, and the type name representation +/// may change. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde", @@ -84,16 +86,29 @@ pub struct Field { ty: T::Type, /// The name of the type of the field as it appears in the source code. type_name: T::String, + /// Should be encode/decoded as a [`Compact`](parity_scale_codec::Compact) field + #[cfg_attr( + feature = "serde", + serde(skip_serializing_if = "is_false", default) + )] + compact: bool, +} + +/// TODO: There must be a better way than this +#[allow(unused)] +fn is_false(v: &bool) -> bool { + !v } -impl IntoCompact for Field { - type Output = Field; +impl IntoFrozen for Field { + type Output = Field; - fn into_compact(self, registry: &mut Registry) -> Self::Output { + fn into_frozen(self, registry: &mut Registry) -> Self::Output { Field { - name: self.name.map(|name| name.into_compact(registry)), + name: self.name.map(|name| name.into_frozen(registry)), ty: registry.register_type(&self.ty), - type_name: self.type_name.into_compact(registry), + type_name: self.type_name.into_frozen(registry), + compact: false, } } } @@ -106,11 +121,13 @@ impl Field { name: Option<&'static str>, ty: MetaType, type_name: &'static str, + // TODO: dp add compact arg (and use it) ) -> Self { Self { name, ty, type_name, + compact: false, } } diff --git a/src/ty/mod.rs b/src/ty/mod.rs index 81ff9805..3dbb29ff 100644 --- a/src/ty/mod.rs +++ b/src/ty/mod.rs @@ -20,11 +20,11 @@ use crate::prelude::{ use crate::{ build::TypeBuilder, form::{ - CompactForm, + FrozenForm, Form, MetaForm, }, - IntoCompact, + IntoFrozen, MetaType, Registry, TypeInfo, @@ -80,16 +80,17 @@ pub struct Type { /// The actual type definition #[cfg_attr(feature = "serde", serde(rename = "def"))] type_def: TypeDef, + // TODO: dp Should we have a `compact` flag here too? Or only here? } -impl IntoCompact for Type { - type Output = Type; +impl IntoFrozen for Type { + type Output = Type; - fn into_compact(self, registry: &mut Registry) -> Self::Output { + fn into_frozen(self, registry: &mut Registry) -> Self::Output { Type { - path: self.path.into_compact(registry), + path: self.path.into_frozen(registry), type_params: registry.register_types(self.type_params), - type_def: self.type_def.into_compact(registry), + type_def: self.type_def.into_frozen(registry), } } } @@ -183,16 +184,16 @@ pub enum TypeDef { Primitive(TypeDefPrimitive), } -impl IntoCompact for TypeDef { - type Output = TypeDef; +impl IntoFrozen for TypeDef { + type Output = TypeDef; - fn into_compact(self, registry: &mut Registry) -> Self::Output { + fn into_frozen(self, registry: &mut Registry) -> Self::Output { match self { - TypeDef::Composite(composite) => composite.into_compact(registry).into(), - TypeDef::Variant(variant) => variant.into_compact(registry).into(), - TypeDef::Sequence(sequence) => sequence.into_compact(registry).into(), - TypeDef::Array(array) => array.into_compact(registry).into(), - TypeDef::Tuple(tuple) => tuple.into_compact(registry).into(), + TypeDef::Composite(composite) => composite.into_frozen(registry).into(), + TypeDef::Variant(variant) => variant.into_frozen(registry).into(), + TypeDef::Sequence(sequence) => sequence.into_frozen(registry).into(), + TypeDef::Array(array) => array.into_frozen(registry).into(), + TypeDef::Tuple(tuple) => tuple.into_frozen(registry).into(), TypeDef::Primitive(primitive) => primitive.into(), } } @@ -253,10 +254,10 @@ pub struct TypeDefArray { type_param: T::Type, } -impl IntoCompact for TypeDefArray { - type Output = TypeDefArray; +impl IntoFrozen for TypeDefArray { + type Output = TypeDefArray; - fn into_compact(self, registry: &mut Registry) -> Self::Output { + fn into_frozen(self, registry: &mut Registry) -> Self::Output { TypeDefArray { len: self.len, type_param: registry.register_type(&self.type_param), @@ -303,10 +304,10 @@ pub struct TypeDefTuple { fields: Vec, } -impl IntoCompact for TypeDefTuple { - type Output = TypeDefTuple; +impl IntoFrozen for TypeDefTuple { + type Output = TypeDefTuple; - fn into_compact(self, registry: &mut Registry) -> Self::Output { + fn into_frozen(self, registry: &mut Registry) -> Self::Output { TypeDefTuple { fields: registry.register_types(self.fields), } @@ -356,10 +357,10 @@ pub struct TypeDefSequence { type_param: T::Type, } -impl IntoCompact for TypeDefSequence { - type Output = TypeDefSequence; +impl IntoFrozen for TypeDefSequence { + type Output = TypeDefSequence; - fn into_compact(self, registry: &mut Registry) -> Self::Output { + fn into_frozen(self, registry: &mut Registry) -> Self::Output { TypeDefSequence { type_param: registry.register_type(&self.type_param), } diff --git a/src/ty/path.rs b/src/ty/path.rs index 3a60df0b..c8f6fa32 100644 --- a/src/ty/path.rs +++ b/src/ty/path.rs @@ -24,12 +24,12 @@ use crate::prelude::{ use crate::{ form::{ - CompactForm, + FrozenForm, Form, MetaForm, }, utils::is_rust_identifier, - IntoCompact, + IntoFrozen, Registry, }; use scale::{ @@ -76,17 +76,17 @@ where } } -impl IntoCompact for Path { - type Output = Path; +impl IntoFrozen for Path { + type Output = Path; - fn into_compact(self, registry: &mut Registry) -> Self::Output { + fn into_frozen(self, registry: &mut Registry) -> Self::Output { Path { - segments: registry.map_into_compact(self.segments), + segments: registry.map_into_frozen(self.segments), } } } -impl Display for Path { +impl Display for Path { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { write!(f, "{}", self.segments.join("::")) } @@ -254,7 +254,7 @@ mod tests { #[test] fn path_display() { let path = - Path::new("Planet", "hello::world").into_compact(&mut Default::default()); + Path::new("Planet", "hello::world").into_frozen(&mut Default::default()); assert_eq!("hello::world::Planet", format!("{}", path)) } } diff --git a/src/ty/variant.rs b/src/ty/variant.rs index f3aa6c64..4f31af47 100644 --- a/src/ty/variant.rs +++ b/src/ty/variant.rs @@ -17,12 +17,12 @@ use crate::prelude::vec::Vec; use crate::{ build::FieldsBuilder, form::{ - CompactForm, + FrozenForm, Form, MetaForm, }, Field, - IntoCompact, + IntoFrozen, Registry, }; use derive_more::From; @@ -94,12 +94,12 @@ pub struct TypeDefVariant { variants: Vec>, } -impl IntoCompact for TypeDefVariant { - type Output = TypeDefVariant; +impl IntoFrozen for TypeDefVariant { + type Output = TypeDefVariant; - fn into_compact(self, registry: &mut Registry) -> Self::Output { + fn into_frozen(self, registry: &mut Registry) -> Self::Output { TypeDefVariant { - variants: registry.map_into_compact(self.variants), + variants: registry.map_into_frozen(self.variants), } } } @@ -173,13 +173,13 @@ pub struct Variant { discriminant: Option, } -impl IntoCompact for Variant { - type Output = Variant; +impl IntoFrozen for Variant { + type Output = Variant; - fn into_compact(self, registry: &mut Registry) -> Self::Output { + fn into_frozen(self, registry: &mut Registry) -> Self::Output { Variant { - name: self.name.into_compact(registry), - fields: registry.map_into_compact(self.fields), + name: self.name.into_frozen(registry), + fields: registry.map_into_frozen(self.fields), discriminant: self.discriminant, } } diff --git a/test_suite/tests/codec.rs b/test_suite/tests/codec.rs index 6d2f398e..0c4bd0e0 100644 --- a/test_suite/tests/codec.rs +++ b/test_suite/tests/codec.rs @@ -25,14 +25,14 @@ use scale::{ Encode, }; use scale_info::{ - form::CompactForm, + form::FrozenForm, prelude::{ num::NonZeroU32, string::String, vec, vec::Vec, }, - IntoCompact as _, + IntoFrozen as _, MetaType, Registry, RegistryReadOnly, diff --git a/test_suite/tests/derive.rs b/test_suite/tests/derive.rs index a3f7b59b..fdb29533 100644 --- a/test_suite/tests/derive.rs +++ b/test_suite/tests/derive.rs @@ -11,7 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - +// #![feature(box_syntax)] #![cfg_attr(not(feature = "std"), no_std)] use scale_info::prelude::boxed::Box; @@ -216,3 +216,157 @@ fn ui_tests() { t.pass("tests/ui/pass_basic_generic_type.rs"); t.pass("tests/ui/pass_complex_generic_self_referential_type.rs"); } + +#[test] +fn substrate_example() { + use scale::{ + // Decode, + Encode, Compact, + }; + use scale_info::prelude::vec::Vec; + // #[allow(unused)] + // type AccountIndex = u32; + /// A multi-format address wrapper for on-chain accounts. + #[allow(unused)] + // #[derive(Encode, Decode, PartialEq, Eq, Clone, TypeInfo)] + #[derive(Encode, TypeInfo)] + #[cfg_attr(feature = "std", derive(Hash))] + pub enum MultiAddress { + /// It's an account ID (pubkey). + Id(AccountId), + /// It's an account index. + // Index(#[codec(compact)] AccountIndex), + Index(Compact), + /// It's some arbitrary raw bytes. + Raw(Vec), + /// It's a 32 byte representation. + Address32([u8; 32]), + /// Its a 20 byte representation. + Address20([u8; 20]), + } + + let _ma = MultiAddress::::Id(32); +} + +// #[ignore] +// fn substrate_example_expanded() { +// use scale::{Decode, Encode, Compact}; +// use scale_info::prelude::vec::Vec; +// /// A multi-format address wrapper for on-chain accounts. +// #[allow(unused)] +// pub enum MultiAddress { +// /// It's an account ID (pubkey). +// Id(AccountId), +// /// It's an account index. +// // Index(#[codec(compact)] AccountIndex), +// Index(Compact), +// /// It's some arbitrary raw bytes. +// Raw(Vec), +// /// It's a 32 byte representation. +// Address32([u8; 32]), +// /// Its a 20 byte representation. +// Address20([u8; 20]), +// } + +// const _IMPL_TYPE_INFO_FOR_MultiAddress: () = { +// impl< +// AccountId: ::scale_info::TypeInfo + 'static, +// AccountIndex: ::scale_info::TypeInfo + 'static, +// > ::scale_info::TypeInfo for MultiAddress +// where +// AccountId: ::scale_info::TypeInfo + 'static, +// AccountIndex: ::scale_info::TypeInfo + 'static, +// { +// type Identity = Self; +// fn type_info() -> ::scale_info::Type { +// ::scale_info::Type::builder() +// .path(::scale_info::Path::new("MultiAddress", "derive")) +// .type_params(<[_]>::into_vec(box [ +// ::scale_info::meta_type::(), +// ::scale_info::meta_type::(), +// ])) +// .variant( +// ::scale_info::build::Variants::with_fields() +// .variant( +// "Id", +// ::scale_info::build::Fields::unnamed() +// .field_of::("AccountId"), +// ) +// .variant( +// "Index", +// ::scale_info::build::Fields::unnamed() +// .field_of::("AccountIndex"), +// ) +// .variant( +// "Raw", +// ::scale_info::build::Fields::unnamed() +// .field_of::>("Vec"), +// ) +// .variant( +// "Address32", +// ::scale_info::build::Fields::unnamed() +// .field_of::<[u8; 32]>("[u8; 32]"), +// ) +// .variant( +// "Address20", +// ::scale_info::build::Fields::unnamed() +// .field_of::<[u8; 20]>("[u8; 20]"), +// ), +// ) +// .into() +// } +// }; +// }; + +// const _: () = { +// #[allow(unknown_lints)] +// #[allow(rust_2018_idioms)] +// extern crate scale as _parity_scale_codec; +// impl _parity_scale_codec::Encode for MultiAddress +// where +// AccountId: _parity_scale_codec::Encode, +// AccountId: _parity_scale_codec::Encode, +// AccountIndex: _parity_scale_codec::HasCompact, +// { +// fn encode_to<__CodecOutputEdqy: _parity_scale_codec::Output>( +// &self, +// __codec_dest_edqy: &mut __CodecOutputEdqy, +// ) { +// match *self { +// MultiAddress::Id(ref aa) => { +// __codec_dest_edqy.push_byte(0usize as u8); +// __codec_dest_edqy.push(aa); +// } +// MultiAddress::Index(ref aa) => { +// __codec_dest_edqy.push_byte(1usize as u8); +// { +// __codec_dest_edqy.push (&<::Type as _parity_scale_codec::EncodeAsRef< '_ , AccountIndex >>::RefType::from(aa)); +// } +// } +// MultiAddress::Raw(ref aa) => { +// __codec_dest_edqy.push_byte(2usize as u8); +// __codec_dest_edqy.push(aa); +// } +// MultiAddress::Address32(ref aa) => { +// __codec_dest_edqy.push_byte(3usize as u8); +// __codec_dest_edqy.push(aa); +// } +// MultiAddress::Address20(ref aa) => { +// __codec_dest_edqy.push_byte(4usize as u8); +// __codec_dest_edqy.push(aa); +// } +// _ => (), +// } +// } +// } +// impl _parity_scale_codec::EncodeLike +// for MultiAddress +// where +// AccountId: _parity_scale_codec::Encode, +// AccountId: _parity_scale_codec::Encode, +// AccountIndex: _parity_scale_codec::HasCompact, +// { +// } +// }; +// let _ma = MultiAddress::::Id(32); +// } diff --git a/test_suite/tests/json.rs b/test_suite/tests/json.rs index 5fd84585..5c3e3d7c 100644 --- a/test_suite/tests/json.rs +++ b/test_suite/tests/json.rs @@ -28,9 +28,9 @@ use pretty_assertions::{ assert_ne, }; use scale_info::{ - form::CompactForm, + form::FrozenForm, meta_type, - IntoCompact as _, + IntoFrozen as _, Registry, TypeInfo, }; @@ -42,7 +42,7 @@ where { let mut registry = Registry::new(); - let ty = T::type_info().into_compact(&mut registry); + let ty = T::type_info().into_frozen(&mut registry); assert_eq!(serde_json::to_value(ty).unwrap(), expected_json,); } From 579f95893e0a73cd06bb555f79922d41bda47e95 Mon Sep 17 00:00:00 2001 From: David Palm Date: Thu, 17 Dec 2020 16:15:59 +0100 Subject: [PATCH 06/28] Docs cleanup and more renames --- src/build.rs | 12 +++++++++++ src/form.rs | 23 ++++++++++---------- src/interner.rs | 9 ++++---- src/lib.rs | 31 ++++++++++++++------------- src/registry.rs | 44 +++++++++++++++++++------------------- src/tests.rs | 12 ++++++++--- src/ty/fields.rs | 14 ++++++++---- src/ty/mod.rs | 1 - test_suite/tests/derive.rs | 1 + test_suite/tests/json.rs | 43 +++++++++++++++++++++++++++++++++++++ 10 files changed, 129 insertions(+), 61 deletions(-) diff --git a/src/build.rs b/src/build.rs index c55cbfad..9667290c 100644 --- a/src/build.rs +++ b/src/build.rs @@ -248,6 +248,18 @@ impl FieldsBuilder { pub fn finalize(self) -> Vec> { self.fields } + + /// Mark last field as compact, meaning that encoding/decoding should be in the [`scale_codec::Compact`] format. + pub fn compact(mut self) -> Self { + self.fields + .iter_mut() + .last() + .and_then(|f| { + f.compact(); + Some(f) + }); + self + } } impl FieldsBuilder { diff --git a/src/form.rs b/src/form.rs index c9302f99..dd6e1f0d 100644 --- a/src/form.rs +++ b/src/form.rs @@ -12,22 +12,21 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Provides some form definitions. +//! Provides form definitions. //! -//! The forms provided here are used to generically communicate the -//! compaction mode a type identifier, type definition or structures -//! that are using these. +//! The forms provided here are used to generically communicate the mode a type +//! identifier, type definition or structure is using. //! //! The default form is the `MetaForm`. //! It uses `MetaType` for communicating type identifiers and thus acts as //! a bridge from runtime to compile time type information. //! -//! The compact form is `FrozenForm` and represents a compact form +//! The `FrozenForm` is a space-efficient representation //! that no longer has any connections to the interning registry and thus -//! can no longer be used in order to retrieve information from the -//! original registry easily. Its sole purpose is for compact serialization. +//! can no longer be used to retrieve information from the +//! original registry. Its sole purpose is for space-efficient serialization. //! -//! Other forms, such as a compact form that is still bound to the registry +//! Other forms, such as a frozen form that is still bound to the registry //! (also via lifetime tracking) are possible but current not needed. use crate::prelude::{ @@ -47,7 +46,7 @@ use serde::Serialize; /// Trait to control the internal structures of type definitions. /// /// This allows for type-level separation between free forms that can be -/// instantiated out of the flux and compact forms that require some sort of +/// instantiated out of the flux and frozen forms that require some sort of /// interning data structures. pub trait Form { /// The type representing the type. @@ -58,8 +57,8 @@ pub trait Form { /// A meta meta-type. /// -/// Allows to be converted into other forms such as compact form -/// through the registry and `IntoCompact`. +/// Allows to be converted into other forms such as frozen form +/// through the registry and `IntoFrozen`. #[cfg_attr(feature = "serde", derive(Serialize))] #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)] pub enum MetaForm {} @@ -69,7 +68,7 @@ impl Form for MetaForm { type String = &'static str; } -/// Compact form that has its lifetime untracked in association to its interner. +/// Frozen form that has its lifetime untracked in association to its interner. /// /// # Note /// diff --git a/src/interner.rs b/src/interner.rs index 61845735..44f2db51 100644 --- a/src/interner.rs +++ b/src/interner.rs @@ -14,12 +14,13 @@ //! Interning data structure and associated symbol definitions. //! -//! The interner is used by the registry in order to compact strings and type +//! The interner is used by the registry in order to deduplicate strings and type //! definitions. Strings are uniquely identified by their contents while types //! are uniquely identified by their respective type identifiers. //! -//! The interners provide a strict ordered sequence of cached (aka interned) -//! elements and is later used for compact serialization within the registry. +//! The interners provide a strict ordered sequence of cached (interned) +//! elements and is later used for space-efficient serialization within the +//! registry. use crate::prelude::{ collections::btree_map::{ @@ -127,7 +128,7 @@ impl Symbol<'_, T> { #[cfg_attr(feature = "serde", derive(Serialize))] #[cfg_attr(feature = "serde", serde(transparent))] pub struct Interner { - /// A mapping from the interned elements to their respective compact + /// A mapping from the interned elements to their respective space-efficient /// identifiers. /// /// The idenfitiers can be used to retrieve information about the original diff --git a/src/lib.rs b/src/lib.rs index fb86ed29..ae35d85d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,38 +29,39 @@ //! //! # Type Information //! -//! Information about types is provided via the [`TypeInfo`](`crate::TypeInfo`) trait. +//! Information about types is provided via the [`TypeInfo`](`crate::TypeInfo`) +//! trait. //! //! This trait should be implemented for all types that are serializable. //! `scale-info` provides implementations for all commonly used Rust standard //! types and a derive macro for implementing of custom types. //! -//! # Compaction Forms +//! # Forms //! -//! There is an expanded form, called [`MetaForm`](`crate::form::MetaForm`) that acts as a bridge -//! between compile-time type information and runtime, in order to easily retrieve all information needed -//! to uniquely identify types. +//! There is an expanded form, called [`MetaForm`](`crate::form::MetaForm`) that +//! acts as a bridge between compile-time type information and runtime, in order +//! to easily retrieve all information needed to uniquely identify types. //! //! The `MetaForm` and its associated `Registry` can be transformed into the -//! space-efficient and universal form by the -//! [`IntoFrozen`](`crate::IntoFrozen`) trait and internally used by the -//! [`Registry`](`crate::Registry`) in order to convert the expanded types into -//! their space-efficient form. +//! space-efficient form by the [`IntoFrozen`](`crate::IntoFrozen`) trait; it is +//! used internally by the [`Registry`](`crate::Registry`) in order to convert +//! the expanded types into their space-efficient form. //! //! # Symbols and Namespaces //! //! To differentiate two types sharing the same name, namespaces are used. -//! Commonly the namespace is equal to the one where the type has been defined in. For Rust prelude -//! types such as [`Option`](`std::option::Option`) and [`Result`](`std::result::Result`) the root -//! namespace (empty namespace) is used. +//! Commonly the namespace is equal to the one where the type has been defined +//! in. For Rust prelude types such as [`Option`](`std::option::Option`) and +//! [`Result`](`std::result::Result`) the root namespace (empty namespace) is +//! used. //! //! To use this library simply use the [`MetaForm`](`crate::form::MetaForm`) //! initially with your own data structures; make them generic over the //! [`Form`](`crate::form::Form`) trait just as has been done in this crate with //! [`TypeInfo`](`crate::TypeInfo`) in order to get a simple implementation of -//! [`IntoFrozen`](`crate::IntoFrozen`). -//! Use a single instance of the [`Registry`](`crate::Registry`) for compaction -//! and provide this registry instance upon serialization. Done. +//! [`IntoFrozen`](`crate::IntoFrozen`). Use a single instance of the +//! [`Registry`](`crate::Registry`) for compaction and provide this registry +//! instance upon serialization. //! //! A usage example can be found in ink! here: //! https://github.com/paritytech/ink/blob/master/abi/src/specs.rs diff --git a/src/registry.rs b/src/registry.rs index 82e63c5f..393c526b 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -12,17 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! The registry has the purpose to compactify types found in type definitions. +//! The registry stores type definitions in a space-efficient manner. //! -//! This is done by deduplicating common types in order to reuse -//! their definitions which can grow arbitrarily large. A type is uniquely +//! This is done by deduplicating common types in order to reuse their +//! definitions which otherwise can grow arbitrarily large. A type is uniquely //! identified by its type identifier that is therefore used to refer to types //! and their definitions. //! //! Types with the same name are uniquely identifiable by introducing -//! namespaces. For this the normal Rust namespace of a type is used where it -//! has been defined. Rust prelude types live within the so-called root -//! namespace that is just empty. +//! namespaces. The normal Rust namespace of a type is used, except for the Rust +//! prelude types that live in the so-called root namespace which is empty. use crate::prelude::{ any::TypeId, @@ -61,7 +60,7 @@ pub trait IntoFrozen { /// The frozen version of `Self`. type Output; - /// "Freezes" `self` by using the registry for caching and compaction. + /// "Freezes" `self` by using the registry for caching. fn into_frozen(self, registry: &mut Registry) -> Self::Output; } @@ -73,18 +72,19 @@ impl IntoFrozen for &'static str { } } -/// The registry for compaction of type identifiers and definitions. +/// The registry for space-efficient storage of type identifiers and +/// definitions. /// -/// The registry consists of a cache for already compactified type identifiers and definitions. +/// The registry consists of a cache for type identifiers and definitions. /// -/// Whenever using the registry to compact a type all of its sub-types -/// are going to be registered recursively as well. A type is a sub-type -/// of another type if it is used by its identifier or structure. +/// When adding a type to the registry, all of its sub-types are registered +/// recursively as well. A type is considered a sub-type of another type if it +/// is used by its identifier or structure. /// /// # Note /// /// A type can be a sub-type of itself. In this case the registry has a builtin -/// mechanism to stop recursion before going into an infinite loop. +/// mechanism to stop recursion and avoid going into an infinite loop. #[derive(Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct Registry { @@ -94,15 +94,15 @@ pub struct Registry { /// for all types found in the `types` field. #[cfg_attr(feature = "serde", serde(skip))] type_table: Interner, - /// The database where registered types actually reside. + /// The database where registered types reside. /// - /// This is going to be serialized upon serlialization. + /// The contents herein is used for serlialization. #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_registry_types"))] types: BTreeMap, Type>, } -/// Serializes the types of the registry by removing their unique IDs -/// and instead serialize them in order of their removed unique ID. +/// Serializes the types of the registry by removing their unique IDs and +/// serializes them in order of their removed unique ID. #[cfg(feature = "serde")] fn serialize_registry_types( types: &BTreeMap, Type>, @@ -174,13 +174,13 @@ impl Registry { pub fn register_type(&mut self, ty: &MetaType) -> UntrackedSymbol { let (inserted, symbol) = self.intern_type_id(ty.type_id()); if inserted { - let compact_id = ty.type_info().into_frozen(self); - self.types.insert(symbol, compact_id); + let frozen_id = ty.type_info().into_frozen(self); + self.types.insert(symbol, frozen_id); } symbol } - /// Calls `register_type` for each `MetaType` in the given `iter` + /// Calls `register_type` for each `MetaType` in the given `iter`. pub fn register_types(&mut self, iter: I) -> Vec> where I: IntoIterator, @@ -190,8 +190,8 @@ impl Registry { .collect::>() } - /// Converts an iterator into a Vec of the equivalent compact - /// representations + /// Converts an iterator into a Vec of the equivalent frozen + /// representations. pub fn map_into_frozen(&mut self, iter: I) -> Vec where I: IntoIterator, diff --git a/src/tests.rs b/src/tests.rs index 73671ae2..92223978 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -155,14 +155,17 @@ fn struct_with_generics() { assert_type!(SelfTyped, expected_type); } +// TODO: dp remove this or make it actually test something #[test] fn struct_with_compact() { use scale::Encode; + // use scale::Compact; #[allow(unused)] #[derive(Encode)] struct Thicc { #[codec(compact)] - stuff: u8 + stuff: u8, + moar: u16, } impl TypeInfo for Thicc { @@ -171,8 +174,11 @@ fn struct_with_compact() { fn type_info() -> Type { Type::builder() .path(Path::new("Thicc", module_path!())) - .composite(Fields::named().field_of::("stuff", "u8")) - .into() + .composite( + Fields::named() + .field_of::("stuff", "u8").compact() + .field_of::("moar", "u16") + ) } } diff --git a/src/ty/fields.rs b/src/ty/fields.rs index e2bea876..8cdb7798 100644 --- a/src/ty/fields.rs +++ b/src/ty/fields.rs @@ -86,7 +86,8 @@ pub struct Field { ty: T::Type, /// The name of the type of the field as it appears in the source code. type_name: T::String, - /// Should be encode/decoded as a [`Compact`](parity_scale_codec::Compact) field + /// This field should be encode/decoded as a + /// [`Compact`](parity_scale_codec::Compact) field #[cfg_attr( feature = "serde", serde(skip_serializing_if = "is_false", default) @@ -95,9 +96,8 @@ pub struct Field { } /// TODO: There must be a better way than this -#[allow(unused)] fn is_false(v: &bool) -> bool { - !v + !(*v) } impl IntoFrozen for Field { @@ -108,7 +108,7 @@ impl IntoFrozen for Field { name: self.name.map(|name| name.into_frozen(registry)), ty: registry.register_type(&self.ty), type_name: self.type_name.into_frozen(registry), - compact: false, + compact: self.compact, } } } @@ -152,6 +152,12 @@ impl Field { { Self::new(None, MetaType::new::(), type_name) } + + /// Set the `compact` property to true, signalling that this type is to be + /// encoded/decoded as [`Compact`](parity_scale_codec::Compact). + pub fn compact(&mut self) { + self.compact = true; + } } impl Field diff --git a/src/ty/mod.rs b/src/ty/mod.rs index 3dbb29ff..fc510506 100644 --- a/src/ty/mod.rs +++ b/src/ty/mod.rs @@ -80,7 +80,6 @@ pub struct Type { /// The actual type definition #[cfg_attr(feature = "serde", serde(rename = "def"))] type_def: TypeDef, - // TODO: dp Should we have a `compact` flag here too? Or only here? } impl IntoFrozen for Type { diff --git a/test_suite/tests/derive.rs b/test_suite/tests/derive.rs index fdb29533..9c16db05 100644 --- a/test_suite/tests/derive.rs +++ b/test_suite/tests/derive.rs @@ -217,6 +217,7 @@ fn ui_tests() { t.pass("tests/ui/pass_complex_generic_self_referential_type.rs"); } +// TODO: dp Make useful or remove #[test] fn substrate_example() { use scale::{ diff --git a/test_suite/tests/json.rs b/test_suite/tests/json.rs index 5c3e3d7c..88ab6762 100644 --- a/test_suite/tests/json.rs +++ b/test_suite/tests/json.rs @@ -196,6 +196,49 @@ fn test_struct() { })); } +#[test] +fn test_struct_with_some_fields_marked_as_compact() { + use scale::Encode; + + // #[derive(TypeInfo, Encode)] + #[derive(Encode)] + struct Dense { + #[codec(compact)] + a: u128, + b: [u8; 32], + #[codec(compact)] + c: u64, + } + use scale_info::{Type, Path, build::Fields}; + impl TypeInfo for Dense { + type Identity = Self; + fn type_info() -> Type { + Type::builder() + .path(Path::new("Dense", module_path!())) + .composite( + Fields::named() + .field_of::("a", "i32").compact() + .field_of::<[u8; 32]>("b", "[u8; 32]") + .field_of::("c", "u64").compact() + ) + // .into() // <–– TODO: dp I don't think we need these `.into()`s anymore. + } + } + + assert_json_for_type::(json![{ + "path": ["json", "Dense"], + "def": { + "composite": { + "fields": [ + { "name": "a", "type": 1, "typeName": "i32", "compact": true }, + { "name": "b", "type": 2, "typeName": "[u8; 32]" }, + { "name": "c", "type": 3, "typeName": "u64", "compact": true }, + ], + }, + } + }]); +} + #[test] fn test_clike_enum() { #[derive(TypeInfo)] From 8333e5a30df57797d104d56603165cb14ae0801c Mon Sep 17 00:00:00 2001 From: David Palm Date: Thu, 17 Dec 2020 16:33:23 +0100 Subject: [PATCH 07/28] Cleanup --- src/build.rs | 12 --- src/tests.rs | 31 -------- src/ty/fields.rs | 21 ----- test_suite/tests/derive.rs | 155 ------------------------------------- test_suite/tests/json.rs | 43 ---------- 5 files changed, 262 deletions(-) diff --git a/src/build.rs b/src/build.rs index 9667290c..c55cbfad 100644 --- a/src/build.rs +++ b/src/build.rs @@ -248,18 +248,6 @@ impl FieldsBuilder { pub fn finalize(self) -> Vec> { self.fields } - - /// Mark last field as compact, meaning that encoding/decoding should be in the [`scale_codec::Compact`] format. - pub fn compact(mut self) -> Self { - self.fields - .iter_mut() - .last() - .and_then(|f| { - f.compact(); - Some(f) - }); - self - } } impl FieldsBuilder { diff --git a/src/tests.rs b/src/tests.rs index 92223978..1552490c 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -154,34 +154,3 @@ fn struct_with_generics() { .composite(Fields::named().field_of::>>("data", "T")); assert_type!(SelfTyped, expected_type); } - -// TODO: dp remove this or make it actually test something -#[test] -fn struct_with_compact() { - use scale::Encode; - // use scale::Compact; - #[allow(unused)] - #[derive(Encode)] - struct Thicc { - #[codec(compact)] - stuff: u8, - moar: u16, - } - - impl TypeInfo for Thicc { - type Identity = Self; - - fn type_info() -> Type { - Type::builder() - .path(Path::new("Thicc", module_path!())) - .composite( - Fields::named() - .field_of::("stuff", "u8").compact() - .field_of::("moar", "u16") - ) - } - } - - fn assert_type_info() {} - assert_type_info::(); -} diff --git a/src/ty/fields.rs b/src/ty/fields.rs index 8cdb7798..d718f2b1 100644 --- a/src/ty/fields.rs +++ b/src/ty/fields.rs @@ -86,18 +86,6 @@ pub struct Field { ty: T::Type, /// The name of the type of the field as it appears in the source code. type_name: T::String, - /// This field should be encode/decoded as a - /// [`Compact`](parity_scale_codec::Compact) field - #[cfg_attr( - feature = "serde", - serde(skip_serializing_if = "is_false", default) - )] - compact: bool, -} - -/// TODO: There must be a better way than this -fn is_false(v: &bool) -> bool { - !(*v) } impl IntoFrozen for Field { @@ -108,7 +96,6 @@ impl IntoFrozen for Field { name: self.name.map(|name| name.into_frozen(registry)), ty: registry.register_type(&self.ty), type_name: self.type_name.into_frozen(registry), - compact: self.compact, } } } @@ -121,13 +108,11 @@ impl Field { name: Option<&'static str>, ty: MetaType, type_name: &'static str, - // TODO: dp add compact arg (and use it) ) -> Self { Self { name, ty, type_name, - compact: false, } } @@ -152,12 +137,6 @@ impl Field { { Self::new(None, MetaType::new::(), type_name) } - - /// Set the `compact` property to true, signalling that this type is to be - /// encoded/decoded as [`Compact`](parity_scale_codec::Compact). - pub fn compact(&mut self) { - self.compact = true; - } } impl Field diff --git a/test_suite/tests/derive.rs b/test_suite/tests/derive.rs index 9c16db05..48fe56a8 100644 --- a/test_suite/tests/derive.rs +++ b/test_suite/tests/derive.rs @@ -216,158 +216,3 @@ fn ui_tests() { t.pass("tests/ui/pass_basic_generic_type.rs"); t.pass("tests/ui/pass_complex_generic_self_referential_type.rs"); } - -// TODO: dp Make useful or remove -#[test] -fn substrate_example() { - use scale::{ - // Decode, - Encode, Compact, - }; - use scale_info::prelude::vec::Vec; - // #[allow(unused)] - // type AccountIndex = u32; - /// A multi-format address wrapper for on-chain accounts. - #[allow(unused)] - // #[derive(Encode, Decode, PartialEq, Eq, Clone, TypeInfo)] - #[derive(Encode, TypeInfo)] - #[cfg_attr(feature = "std", derive(Hash))] - pub enum MultiAddress { - /// It's an account ID (pubkey). - Id(AccountId), - /// It's an account index. - // Index(#[codec(compact)] AccountIndex), - Index(Compact), - /// It's some arbitrary raw bytes. - Raw(Vec), - /// It's a 32 byte representation. - Address32([u8; 32]), - /// Its a 20 byte representation. - Address20([u8; 20]), - } - - let _ma = MultiAddress::::Id(32); -} - -// #[ignore] -// fn substrate_example_expanded() { -// use scale::{Decode, Encode, Compact}; -// use scale_info::prelude::vec::Vec; -// /// A multi-format address wrapper for on-chain accounts. -// #[allow(unused)] -// pub enum MultiAddress { -// /// It's an account ID (pubkey). -// Id(AccountId), -// /// It's an account index. -// // Index(#[codec(compact)] AccountIndex), -// Index(Compact), -// /// It's some arbitrary raw bytes. -// Raw(Vec), -// /// It's a 32 byte representation. -// Address32([u8; 32]), -// /// Its a 20 byte representation. -// Address20([u8; 20]), -// } - -// const _IMPL_TYPE_INFO_FOR_MultiAddress: () = { -// impl< -// AccountId: ::scale_info::TypeInfo + 'static, -// AccountIndex: ::scale_info::TypeInfo + 'static, -// > ::scale_info::TypeInfo for MultiAddress -// where -// AccountId: ::scale_info::TypeInfo + 'static, -// AccountIndex: ::scale_info::TypeInfo + 'static, -// { -// type Identity = Self; -// fn type_info() -> ::scale_info::Type { -// ::scale_info::Type::builder() -// .path(::scale_info::Path::new("MultiAddress", "derive")) -// .type_params(<[_]>::into_vec(box [ -// ::scale_info::meta_type::(), -// ::scale_info::meta_type::(), -// ])) -// .variant( -// ::scale_info::build::Variants::with_fields() -// .variant( -// "Id", -// ::scale_info::build::Fields::unnamed() -// .field_of::("AccountId"), -// ) -// .variant( -// "Index", -// ::scale_info::build::Fields::unnamed() -// .field_of::("AccountIndex"), -// ) -// .variant( -// "Raw", -// ::scale_info::build::Fields::unnamed() -// .field_of::>("Vec"), -// ) -// .variant( -// "Address32", -// ::scale_info::build::Fields::unnamed() -// .field_of::<[u8; 32]>("[u8; 32]"), -// ) -// .variant( -// "Address20", -// ::scale_info::build::Fields::unnamed() -// .field_of::<[u8; 20]>("[u8; 20]"), -// ), -// ) -// .into() -// } -// }; -// }; - -// const _: () = { -// #[allow(unknown_lints)] -// #[allow(rust_2018_idioms)] -// extern crate scale as _parity_scale_codec; -// impl _parity_scale_codec::Encode for MultiAddress -// where -// AccountId: _parity_scale_codec::Encode, -// AccountId: _parity_scale_codec::Encode, -// AccountIndex: _parity_scale_codec::HasCompact, -// { -// fn encode_to<__CodecOutputEdqy: _parity_scale_codec::Output>( -// &self, -// __codec_dest_edqy: &mut __CodecOutputEdqy, -// ) { -// match *self { -// MultiAddress::Id(ref aa) => { -// __codec_dest_edqy.push_byte(0usize as u8); -// __codec_dest_edqy.push(aa); -// } -// MultiAddress::Index(ref aa) => { -// __codec_dest_edqy.push_byte(1usize as u8); -// { -// __codec_dest_edqy.push (&<::Type as _parity_scale_codec::EncodeAsRef< '_ , AccountIndex >>::RefType::from(aa)); -// } -// } -// MultiAddress::Raw(ref aa) => { -// __codec_dest_edqy.push_byte(2usize as u8); -// __codec_dest_edqy.push(aa); -// } -// MultiAddress::Address32(ref aa) => { -// __codec_dest_edqy.push_byte(3usize as u8); -// __codec_dest_edqy.push(aa); -// } -// MultiAddress::Address20(ref aa) => { -// __codec_dest_edqy.push_byte(4usize as u8); -// __codec_dest_edqy.push(aa); -// } -// _ => (), -// } -// } -// } -// impl _parity_scale_codec::EncodeLike -// for MultiAddress -// where -// AccountId: _parity_scale_codec::Encode, -// AccountId: _parity_scale_codec::Encode, -// AccountIndex: _parity_scale_codec::HasCompact, -// { -// } -// }; -// let _ma = MultiAddress::::Id(32); -// } diff --git a/test_suite/tests/json.rs b/test_suite/tests/json.rs index 88ab6762..5c3e3d7c 100644 --- a/test_suite/tests/json.rs +++ b/test_suite/tests/json.rs @@ -196,49 +196,6 @@ fn test_struct() { })); } -#[test] -fn test_struct_with_some_fields_marked_as_compact() { - use scale::Encode; - - // #[derive(TypeInfo, Encode)] - #[derive(Encode)] - struct Dense { - #[codec(compact)] - a: u128, - b: [u8; 32], - #[codec(compact)] - c: u64, - } - use scale_info::{Type, Path, build::Fields}; - impl TypeInfo for Dense { - type Identity = Self; - fn type_info() -> Type { - Type::builder() - .path(Path::new("Dense", module_path!())) - .composite( - Fields::named() - .field_of::("a", "i32").compact() - .field_of::<[u8; 32]>("b", "[u8; 32]") - .field_of::("c", "u64").compact() - ) - // .into() // <–– TODO: dp I don't think we need these `.into()`s anymore. - } - } - - assert_json_for_type::(json![{ - "path": ["json", "Dense"], - "def": { - "composite": { - "fields": [ - { "name": "a", "type": 1, "typeName": "i32", "compact": true }, - { "name": "b", "type": 2, "typeName": "[u8; 32]" }, - { "name": "c", "type": 3, "typeName": "u64", "compact": true }, - ], - }, - } - }]); -} - #[test] fn test_clike_enum() { #[derive(TypeInfo)] From 2818f7bc398fdf44318ca679cbb7141891a17f34 Mon Sep 17 00:00:00 2001 From: David Palm Date: Thu, 17 Dec 2020 16:37:13 +0100 Subject: [PATCH 08/28] More cleanup --- README.md | 8 ++++---- test_suite/tests/derive.rs | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index c700249c..f9ff3e8a 100644 --- a/README.md +++ b/README.md @@ -200,12 +200,12 @@ identifiers. All concrete `TypeInfo` structures have two forms: - One meta form (`MetaType`) that acts as a bridge to other forms -- A compact form that is later to be serialized. +- A frozen form suitable for serialization. -The `IntoCompact` trait must also be implemented in order to compact a type -definition using an instance of a type registry. +The `IntoFrozen` trait must also be implemented in order prepare a type +definition for serialization using an instance of the type registry. -After compactification all type definitions are stored in the type registry. +After transformation all type definitions are stored in the type registry. Note that the type registry should be serialized as part of the metadata structure where the registered types are utilized to allow consumers to resolve the types. diff --git a/test_suite/tests/derive.rs b/test_suite/tests/derive.rs index 48fe56a8..83eb3749 100644 --- a/test_suite/tests/derive.rs +++ b/test_suite/tests/derive.rs @@ -11,7 +11,6 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -// #![feature(box_syntax)] #![cfg_attr(not(feature = "std"), no_std)] use scale_info::prelude::boxed::Box; From e03a2cd38fb7643cb00fcc766e94e8b044c4b16f Mon Sep 17 00:00:00 2001 From: David Palm Date: Thu, 17 Dec 2020 19:25:50 +0100 Subject: [PATCH 09/28] obey the fmt --- src/registry.rs | 2 +- src/ty/composite.rs | 2 +- src/ty/fields.rs | 2 +- src/ty/mod.rs | 2 +- src/ty/path.rs | 2 +- src/ty/variant.rs | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/registry.rs b/src/registry.rs index 8e8a16cc..9dc5ee99 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -34,9 +34,9 @@ use crate::prelude::{ use crate::{ form::{ - FrozenForm, Form, FormString, + FrozenForm, }, interner::{ Interner, diff --git a/src/ty/composite.rs b/src/ty/composite.rs index 2ec039d8..4d3923da 100644 --- a/src/ty/composite.rs +++ b/src/ty/composite.rs @@ -16,8 +16,8 @@ use crate::prelude::vec::Vec; use crate::{ form::{ - FrozenForm, Form, + FrozenForm, MetaForm, }, Field, diff --git a/src/ty/fields.rs b/src/ty/fields.rs index d718f2b1..803699ed 100644 --- a/src/ty/fields.rs +++ b/src/ty/fields.rs @@ -14,8 +14,8 @@ use crate::{ form::{ - FrozenForm, Form, + FrozenForm, MetaForm, }, IntoFrozen, diff --git a/src/ty/mod.rs b/src/ty/mod.rs index fc510506..313d2289 100644 --- a/src/ty/mod.rs +++ b/src/ty/mod.rs @@ -20,8 +20,8 @@ use crate::prelude::{ use crate::{ build::TypeBuilder, form::{ - FrozenForm, Form, + FrozenForm, MetaForm, }, IntoFrozen, diff --git a/src/ty/path.rs b/src/ty/path.rs index c8f6fa32..c3116881 100644 --- a/src/ty/path.rs +++ b/src/ty/path.rs @@ -24,8 +24,8 @@ use crate::prelude::{ use crate::{ form::{ - FrozenForm, Form, + FrozenForm, MetaForm, }, utils::is_rust_identifier, diff --git a/src/ty/variant.rs b/src/ty/variant.rs index 4f31af47..6dbdb90a 100644 --- a/src/ty/variant.rs +++ b/src/ty/variant.rs @@ -17,8 +17,8 @@ use crate::prelude::vec::Vec; use crate::{ build::FieldsBuilder, form::{ - FrozenForm, Form, + FrozenForm, MetaForm, }, Field, From 3a95663438b63b721e37d62f89c00fd4b3cf90d4 Mon Sep 17 00:00:00 2001 From: David Palm Date: Thu, 17 Dec 2020 20:48:01 +0100 Subject: [PATCH 10/28] Add a `compact` flag to `Field` to indicate that this type is to be encoded/decoded as a SCALE Compact type --- derive/src/lib.rs | 1 - src/build.rs | 9 ++++++++ src/registry.rs | 1 - src/tests.rs | 1 - src/ty/fields.rs | 21 ++++++++++++++++-- test_suite/tests/json.rs | 48 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 76 insertions(+), 5 deletions(-) diff --git a/derive/src/lib.rs b/derive/src/lib.rs index c12d8c2d..89aeed8e 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -92,7 +92,6 @@ fn generate_type(input: TokenStream2) -> Result { .path(::scale_info::Path::new(stringify!(#ident), module_path!())) .type_params(::scale_info::prelude::vec![ #( #generic_type_ids ),* ]) .#build_type - .into() } } }; diff --git a/src/build.rs b/src/build.rs index c55cbfad..39065785 100644 --- a/src/build.rs +++ b/src/build.rs @@ -248,6 +248,15 @@ impl FieldsBuilder { pub fn finalize(self) -> Vec> { self.fields } + + /// Mark last field as compact, meaning that encoding/decoding should be in the [`scale_codec::Compact`] format. + pub fn compact(mut self) -> Self { + self.fields.iter_mut().last().and_then(|f| { + f.compact(); + Some(f) + }); + self + } } impl FieldsBuilder { diff --git a/src/registry.rs b/src/registry.rs index 9dc5ee99..68abf220 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -303,7 +303,6 @@ mod tests { "&mut RecursiveRefs", ), ) - .into() } } diff --git a/src/tests.rs b/src/tests.rs index 1552490c..0f036073 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -134,7 +134,6 @@ fn struct_with_generics() { .path(Path::new("MyStruct", module_path!())) .type_params(tuple_meta_type!(T)) .composite(Fields::named().field_of::("data", "T")) - .into() } } diff --git a/src/ty/fields.rs b/src/ty/fields.rs index 803699ed..1cb1a853 100644 --- a/src/ty/fields.rs +++ b/src/ty/fields.rs @@ -86,6 +86,14 @@ pub struct Field { ty: T::Type, /// The name of the type of the field as it appears in the source code. type_name: T::String, + /// This field should be encode/decoded as a + /// [`Compact`](parity_scale_codec::Compact) field + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "is_false", default))] + compact: bool, +} + +fn is_false(v: &bool) -> bool { + !v } impl IntoFrozen for Field { @@ -96,6 +104,7 @@ impl IntoFrozen for Field { name: self.name.map(|name| name.into_frozen(registry)), ty: registry.register_type(&self.ty), type_name: self.type_name.into_frozen(registry), + compact: self.compact, } } } @@ -108,11 +117,13 @@ impl Field { name: Option<&'static str>, ty: MetaType, type_name: &'static str, + compact: bool, ) -> Self { Self { name, ty, type_name, + compact, } } @@ -124,7 +135,7 @@ impl Field { where T: TypeInfo + ?Sized + 'static, { - Self::new(Some(name), MetaType::new::(), type_name) + Self::new(Some(name), MetaType::new::(), type_name, false) } /// Creates a new unnamed field. @@ -135,7 +146,7 @@ impl Field { where T: TypeInfo + ?Sized + 'static, { - Self::new(None, MetaType::new::(), type_name) + Self::new(None, MetaType::new::(), type_name, false) } } @@ -161,4 +172,10 @@ where pub fn type_name(&self) -> &T::String { &self.type_name } + + /// Set the `compact` property to true, signalling that this type is to be + /// encoded/decoded as a [`parity_scale_codec::Compact`]. + pub fn compact(&mut self) { + self.compact = true; + } } diff --git a/test_suite/tests/json.rs b/test_suite/tests/json.rs index 5c3e3d7c..b176eee6 100644 --- a/test_suite/tests/json.rs +++ b/test_suite/tests/json.rs @@ -196,6 +196,54 @@ fn test_struct() { })); } +#[test] +fn test_struct_with_some_fields_marked_as_compact() { + use scale::Encode; + + // #[derive(TypeInfo, Encode)] + #[derive(Encode)] + struct Dense { + #[codec(compact)] + a: u128, + b: [u8; 32], + #[codec(compact)] + c: u64, + } + use scale_info::{ + build::Fields, + Path, + Type, + }; + impl TypeInfo for Dense { + type Identity = Self; + fn type_info() -> Type { + Type::builder() + .path(Path::new("Dense", module_path!())) + .composite( + Fields::named() + .field_of::("a", "i32") + .compact() + .field_of::<[u8; 32]>("b", "[u8; 32]") + .field_of::("c", "u64") + .compact(), + ) + } + } + + assert_json_for_type::(json![{ + "path": ["json", "Dense"], + "def": { + "composite": { + "fields": [ + { "name": "a", "type": 1, "typeName": "i32", "compact": true }, + { "name": "b", "type": 2, "typeName": "[u8; 32]" }, + { "name": "c", "type": 3, "typeName": "u64", "compact": true }, + ], + }, + } + }]); +} + #[test] fn test_clike_enum() { #[derive(TypeInfo)] From 004e1076239d1e5506a2d7e61646d9b65e2b1f6e Mon Sep 17 00:00:00 2001 From: David Palm Date: Thu, 17 Dec 2020 21:28:53 +0100 Subject: [PATCH 11/28] Clippy warnings --- src/build.rs | 4 ++-- src/ty/fields.rs | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/build.rs b/src/build.rs index 39065785..5778912e 100644 --- a/src/build.rs +++ b/src/build.rs @@ -251,9 +251,9 @@ impl FieldsBuilder { /// Mark last field as compact, meaning that encoding/decoding should be in the [`scale_codec::Compact`] format. pub fn compact(mut self) -> Self { - self.fields.iter_mut().last().and_then(|f| { + self.fields.iter_mut().last().map(|f| { f.compact(); - Some(f) + f }); self } diff --git a/src/ty/fields.rs b/src/ty/fields.rs index 1cb1a853..d68a943b 100644 --- a/src/ty/fields.rs +++ b/src/ty/fields.rs @@ -92,6 +92,7 @@ pub struct Field { compact: bool, } +#[allow(dead_code)] fn is_false(v: &bool) -> bool { !v } From 93a9aebfc235db66d19872b417f9d0d163a95ba5 Mon Sep 17 00:00:00 2001 From: David Palm Date: Thu, 17 Dec 2020 21:34:29 +0100 Subject: [PATCH 12/28] Acommodate older clippy --- src/ty/fields.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ty/fields.rs b/src/ty/fields.rs index d68a943b..404c5514 100644 --- a/src/ty/fields.rs +++ b/src/ty/fields.rs @@ -92,9 +92,11 @@ pub struct Field { compact: bool, } +// Need to obey the required serde signature here +#[allow(clippy::trivially_copy_pass_by_ref)] #[allow(dead_code)] -fn is_false(v: &bool) -> bool { - !v +const fn is_false(v: &bool) -> bool { + !(*v) } impl IntoFrozen for Field { From 6569e507ee8ab9087e0b464dd5477c31b0e13b7f Mon Sep 17 00:00:00 2001 From: David Palm Date: Mon, 28 Dec 2020 17:15:20 +0100 Subject: [PATCH 13/28] Derive (scale) compact fields --- derive/src/lib.rs | 33 ++++++++++++++++++++--- test_suite/tests/derive.rs | 55 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 83 insertions(+), 5 deletions(-) diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 89aeed8e..447bad83 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -37,6 +37,7 @@ use syn::{ }, punctuated::Punctuated, token::Comma, + AttrStyle, Data, DataEnum, DataStruct, @@ -46,6 +47,9 @@ use syn::{ Field, Fields, Lit, + Meta, + MetaList, + NestedMeta, Variant, }; @@ -107,20 +111,43 @@ fn generate_fields(fields: &FieldsList) -> Vec { .map(|f| { let (ty, ident) = (&f.ty, &f.ident); let type_name = clean_type_string("e!(#ty).to_string()); - + let compact = if is_compact(f) { + quote! { + .compact() + } + } else { + quote! {} + }; if let Some(i) = ident { quote! { - .field_of::<#ty>(stringify!(#i), #type_name) + .field_of::<#ty>(stringify!(#i), #type_name) #compact } } else { quote! { - .field_of::<#ty>(#type_name) + .field_of::<#ty>(#type_name) #compact } } }) .collect() } +/// Look for a `#[codec(compact)]` outer attribute. +fn is_compact(f: &Field) -> bool { + f.attrs.iter().any(|attr| { + let mut is_compact = false; + if attr.style == AttrStyle::Outer && attr.path.is_ident("codec") { + if let Ok(Meta::List(MetaList { nested, .. })) = attr.parse_meta() { + if let Some(NestedMeta::Meta(Meta::Path(path))) = nested.iter().next() { + if path.is_ident("compact") { + is_compact = true; + } + } + } + } + is_compact + }) +} + fn clean_type_string(input: &str) -> String { input .replace(" ::", "::") diff --git a/test_suite/tests/derive.rs b/test_suite/tests/derive.rs index 83eb3749..a6871e1b 100644 --- a/test_suite/tests/derive.rs +++ b/test_suite/tests/derive.rs @@ -13,11 +13,11 @@ // limitations under the License. #![cfg_attr(not(feature = "std"), no_std)] -use scale_info::prelude::boxed::Box; - use pretty_assertions::assert_eq; +use scale::Encode; use scale_info::{ build::*, + prelude::boxed::Box, tuple_meta_type, Path, Type, @@ -204,6 +204,57 @@ fn associated_types_derive_without_bounds() { assert_type!(Assoc, struct_type); } +#[test] +fn scale_compact_types_work_in_structs() { + #[allow(unused)] + #[derive(Encode, TypeInfo)] + struct Dense { + a: u8, + #[codec(compact)] + b: u16, + } + + let dense = Type::builder() + .path(Path::new("Dense", "derive")) + .composite( + Fields::named() + .field_of::("a", "u8") + .field_of::("b", "u16") + .compact(), + ); + + assert_type!(Dense, dense); +} + +#[test] +fn scale_compact_types_work_in_enums() { + #[allow(unused)] + #[derive(Encode, TypeInfo)] + enum MutilatedMultiAddress { + Id(AccountId), + Index(#[codec(compact)] AccountIndex), + Address32([u8; 32]), + } + + let ty = Type::builder() + .path(Path::new("MutilatedMultiAddress", "derive")) + .type_params(tuple_meta_type!(u8, u16)) + .variant( + Variants::with_fields() + .variant("Id", Fields::unnamed().field_of::("AccountId")) + .variant( + "Index", + Fields::unnamed().field_of::("AccountIndex").compact(), + ) + .variant( + "Address32", + Fields::unnamed().field_of::<[u8; 32]>("[u8; 32]"), + ), + ); + + assert_type!(MutilatedMultiAddress, ty); +} + #[rustversion::nightly] #[test] fn ui_tests() { From 09c12417f02cb35a766fe4196c9407d6c05eb067 Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 8 Jan 2021 15:03:34 +0100 Subject: [PATCH 14/28] WIP --- derive/src/lib.rs | 28 +++++++++++-------- src/build.rs | 32 +++++++++++++++------ src/impls.rs | 14 ++++++++++ src/meta_type.rs | 3 +- src/tests.rs | 11 ++++++++ src/ty/fields.rs | 35 +++++++++-------------- src/ty/mod.rs | 57 ++++++++++++++++++++++++++++++++++++++ test_suite/tests/derive.rs | 25 +++++++++++++++-- test_suite/tests/json.rs | 8 ++---- 9 files changed, 163 insertions(+), 50 deletions(-) diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 8f2a02ff..f156b7dd 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -111,20 +111,26 @@ fn generate_fields(fields: &FieldsList) -> Vec { .map(|f| { let (ty, ident) = (&f.ty, &f.ident); let type_name = clean_type_string("e!(#ty).to_string()); - let compact = if is_compact(f) { - quote! { - .compact() - } - } else { - quote! {} - }; + // TODO: make this prettier if let Some(i) = ident { - quote! { - .field_of::<#ty>(stringify!(#i), #type_name) #compact + if is_compact(f) { + quote! { + .compact_of::<#ty>(stringify!(#i), #type_name) + } + } else { + quote! { + .field_of::<#ty>(stringify!(#i), #type_name) + } } } else { - quote! { - .field_of::<#ty>(#type_name) #compact + if is_compact(f) { + quote! { + .compact_of::<#ty>(#type_name) + } + } else { + quote! { + .field_of::<#ty>(#type_name) + } } } }) diff --git a/src/build.rs b/src/build.rs index 388a8916..935e520e 100644 --- a/src/build.rs +++ b/src/build.rs @@ -128,6 +128,7 @@ use crate::{ Path, Type, TypeDef, + TypeDefCompact, TypeDefComposite, TypeDefVariant, TypeInfo, @@ -188,6 +189,11 @@ impl TypeBuilder { pub fn composite(self, fields: FieldsBuilder) -> Type { self.build(TypeDefComposite::new(fields.finalize())) } + + /// Construct a [`Compact`] type, i.e. a `scale::Compact` + pub fn compact(self) -> Type { + self.build(TypeDefCompact::new(MetaType::new::())) + } } impl TypeBuilder { @@ -249,14 +255,6 @@ impl FieldsBuilder { self.fields } - /// Mark last field as compact, meaning that encoding/decoding should be in the [`scale_codec::Compact`] format. - pub fn compact(mut self) -> Self { - self.fields.iter_mut().last().map(|f| { - f.compact(); - f - }); - self - } } impl FieldsBuilder { @@ -268,6 +266,15 @@ impl FieldsBuilder { self.fields.push(Field::named_of::(name, type_name)); self } + + /// Add a named, [`Compact`] field of type `T`. + pub fn compact_of(mut self, name: &'static str, type_name: &'static str) -> Self + where + T: TypeInfo + 'static, + { + self.fields.push(Field::compact_of::(Some(name), type_name)); + self + } } impl FieldsBuilder { @@ -279,6 +286,15 @@ impl FieldsBuilder { self.fields.push(Field::unnamed_of::(type_name)); self } + + /// Add an unnamed, [`Compact`] field of type `T`. + pub fn compact_of(mut self, type_name: &'static str) -> Self + where + T: TypeInfo + 'static, + { + self.fields.push(Field::compact_of::(None, type_name)); + self + } } /// Build a type with no variants. diff --git a/src/impls.rs b/src/impls.rs index 2cb3395b..bf6c416d 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -249,3 +249,17 @@ where .composite(Fields::unit()) } } + +impl TypeInfo for scale::Compact +where + T: TypeInfo + 'static, +{ + type Identity = Self; + fn type_info() -> Type { + Type::builder() + // TODO: do we need `Compact` in the prelude perhaps? + .path(Path::from_segments(vec!["scale", "Compact"]).expect("TODO proof")) + .type_params(tuple_meta_type!(T)) + .compact::() + } +} diff --git a/src/meta_type.rs b/src/meta_type.rs index a0a6cd7f..22566857 100644 --- a/src/meta_type.rs +++ b/src/meta_type.rs @@ -96,7 +96,8 @@ impl MetaType { } } - /// Creates a new meta types from the type of the given reference. + // TODO: is this used anywhere? + /// Creates a new meta type from the type of the given reference. pub fn of(_elem: &T) -> Self where T: TypeInfo + ?Sized + 'static, diff --git a/src/tests.rs b/src/tests.rs index 965a0182..2f97db2e 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -83,6 +83,17 @@ fn prelude_items() { ); } +#[test] +fn compact_types() { + assert_type!( + scale::Compact, + Type::builder() + .path(Path::from_segments(vec!["scale", "Compact"]).unwrap()) + .type_params(tuple_meta_type!(i32)) + .compact::() + ); +} + #[test] fn tuple_primitives() { // unit diff --git a/src/ty/fields.rs b/src/ty/fields.rs index 47abb036..b360ce7a 100644 --- a/src/ty/fields.rs +++ b/src/ty/fields.rs @@ -86,17 +86,6 @@ pub struct Field { ty: T::Type, /// The name of the type of the field as it appears in the source code. type_name: T::String, - /// This field should be encode/decoded as a - /// [`Compact`](parity_scale_codec::Compact) field - #[cfg_attr(feature = "serde", serde(skip_serializing_if = "is_false", default))] - compact: bool, -} - -// Need to obey the required serde signature here -#[allow(clippy::trivially_copy_pass_by_ref)] -#[allow(dead_code)] -const fn is_false(v: &bool) -> bool { - !(*v) } impl IntoPortable for Field { @@ -107,7 +96,6 @@ impl IntoPortable for Field { name: self.name.map(|name| name.into_portable(registry)), ty: registry.register_type(&self.ty), type_name: self.type_name.into_portable(registry), - compact: self.compact, } } } @@ -120,13 +108,11 @@ impl Field { name: Option<&'static str>, ty: MetaType, type_name: &'static str, - compact: bool, ) -> Self { Self { name, ty, type_name, - compact, } } @@ -138,7 +124,7 @@ impl Field { where T: TypeInfo + ?Sized + 'static, { - Self::new(Some(name), MetaType::new::(), type_name, false) + Self::new(Some(name), MetaType::new::(), type_name) } /// Creates a new unnamed field. @@ -149,7 +135,18 @@ impl Field { where T: TypeInfo + ?Sized + 'static, { - Self::new(None, MetaType::new::(), type_name, false) + Self::new(None, MetaType::new::(), type_name) + } + + /// Creates a new [`Compact`] field. + pub fn compact_of(name: Option<&'static str>, type_name: &'static str) -> Field + where + T: TypeInfo + 'static, + { + // TODO: using `scale::Compact` here like this makes the `impl + // TypeInfo for Compact` kick in, but not sure that is the right + // thing to do. :/ + Self::new(name, MetaType::new::>(), type_name) } } @@ -175,10 +172,4 @@ where pub fn type_name(&self) -> &T::String { &self.type_name } - - /// Set the `compact` property to true, signalling that this type is to be - /// encoded/decoded as a [`parity_scale_codec::Compact`]. - pub fn compact(&mut self) { - self.compact = true; - } } diff --git a/src/ty/mod.rs b/src/ty/mod.rs index 886bb918..b465ebf5 100644 --- a/src/ty/mod.rs +++ b/src/ty/mod.rs @@ -118,6 +118,17 @@ impl From for Type { } } +impl From for Type { + fn from(compact: TypeDefCompact) -> Self { + Self::new( + // TODO: should the path be empty? Doesn't seem to be used anywhere. + Path::from_segments(vec!["what", "goes", "here"]).expect("TODO proof here"), + vec![compact.type_param], + compact, + ) + } +} + impl Type { /// Create a [`TypeBuilder`](`crate::build::TypeBuilder`) the public API for constructing a [`Type`] pub fn builder() -> TypeBuilder { @@ -181,6 +192,8 @@ pub enum TypeDef { Tuple(TypeDefTuple), /// A Rust primitive type. Primitive(TypeDefPrimitive), + /// A type using the [`Compact`] encoding + Compact(TypeDefCompact), } impl IntoPortable for TypeDef { @@ -194,6 +207,7 @@ impl IntoPortable for TypeDef { TypeDef::Array(array) => array.into_portable(registry).into(), TypeDef::Tuple(tuple) => tuple.into_portable(registry).into(), TypeDef::Primitive(primitive) => primitive.into(), + TypeDef::Compact(compact) => compact.into_portable(registry).into(), } } } @@ -395,3 +409,46 @@ where &self.type_param } } + +/// A type wrapped in [`Compact`]. +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, Debug)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr( + feature = "serde", + serde(bound( + serialize = "T::Type: Serialize, T::String: Serialize", + deserialize = "T::Type: DeserializeOwned, T::String: DeserializeOwned", + )) +)] +pub struct TypeDefCompact { + /// The type wrapped in [`Compact`], i.e. the `T` in `Compact`. + #[cfg_attr(feature = "serde", serde(rename = "type"))] + type_param: T::Type, +} + +impl IntoPortable for TypeDefCompact { + type Output = TypeDefCompact; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + TypeDefCompact { + type_param: registry.register_type(&self.type_param), + } + } +} + +impl TypeDefCompact { + /// Creates a new type wrapped in [`Compact`]. + pub fn new(type_param: MetaType) -> Self { + Self { type_param } + } +} + +impl TypeDefCompact +where + T: Form, +{ + /// Returns the [`Compact`] wrapped type, i.e. the `T` in `Compact`. + pub fn type_param(&self) -> &T::Type { + &self.type_param + } +} diff --git a/test_suite/tests/derive.rs b/test_suite/tests/derive.rs index c2b3f824..9ac5dd70 100644 --- a/test_suite/tests/derive.rs +++ b/test_suite/tests/derive.rs @@ -219,13 +219,32 @@ fn scale_compact_types_work_in_structs() { .composite( Fields::named() .field_of::("a", "u8") - .field_of::("b", "u16") - .compact(), + .field_of::>("b", "u16") ); assert_type!(Dense, dense); } +// TODO: both this test and the one above pass, which means something's not +// right. +#[test] +fn wip() { + #[allow(unused)] + #[derive(Encode, TypeInfo)] + struct A { + a: u8, + #[codec(compact)] + b: u16, + } + let ty = Type::builder() + .path(Path::new("A", "derive")) + .composite( + Fields::named() + .field_of::("a", "u8") + .compact_of::("b", "u16") + ); + assert_type!(A, ty); +} #[test] fn scale_compact_types_work_in_enums() { #[allow(unused)] @@ -244,7 +263,7 @@ fn scale_compact_types_work_in_enums() { .variant("Id", Fields::unnamed().field_of::("AccountId")) .variant( "Index", - Fields::unnamed().field_of::("AccountIndex").compact(), + Fields::unnamed().compact_of::("AccountIndex"), ) .variant( "Address32", diff --git a/test_suite/tests/json.rs b/test_suite/tests/json.rs index 2a29ebe3..16188171 100644 --- a/test_suite/tests/json.rs +++ b/test_suite/tests/json.rs @@ -222,15 +222,13 @@ fn test_struct_with_some_fields_marked_as_compact() { .path(Path::new("Dense", module_path!())) .composite( Fields::named() - .field_of::("a", "i32") - .compact() + .compact_of::("a", "i32") .field_of::<[u8; 32]>("b", "[u8; 32]") - .field_of::("c", "u64") - .compact(), + .compact_of::("c", "u64") ) } } - + // TODO: this is the correct JSON imo, but this fails. assert_json_for_type::(json![{ "path": ["json", "Dense"], "def": { From 55f1413d1cf8cda1e3926fbd72be36a8ce9e8ea5 Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 8 Jan 2021 17:44:24 +0100 Subject: [PATCH 15/28] Sort out the TypeInfo impl for Compact (ty @ascjones!) Add test that illustrates the registry in the presence of compact types. --- src/build.rs | 4 +- src/impls.rs | 7 +--- src/ty/mod.rs | 4 +- test_suite/tests/derive.rs | 8 ++-- test_suite/tests/json.rs | 76 ++++++++++++++++++++++++++++++++++---- 5 files changed, 78 insertions(+), 21 deletions(-) diff --git a/src/build.rs b/src/build.rs index 935e520e..f37c991f 100644 --- a/src/build.rs +++ b/src/build.rs @@ -254,7 +254,6 @@ impl FieldsBuilder { pub fn finalize(self) -> Vec> { self.fields } - } impl FieldsBuilder { @@ -272,7 +271,8 @@ impl FieldsBuilder { where T: TypeInfo + 'static, { - self.fields.push(Field::compact_of::(Some(name), type_name)); + self.fields + .push(Field::compact_of::(Some(name), type_name)); self } } diff --git a/src/impls.rs b/src/impls.rs index bf6c416d..5742dbb4 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -28,6 +28,7 @@ use crate::{ Path, Type, TypeDefArray, + TypeDefCompact, TypeDefPrimitive, TypeDefSequence, TypeDefTuple, @@ -256,10 +257,6 @@ where { type Identity = Self; fn type_info() -> Type { - Type::builder() - // TODO: do we need `Compact` in the prelude perhaps? - .path(Path::from_segments(vec!["scale", "Compact"]).expect("TODO proof")) - .type_params(tuple_meta_type!(T)) - .compact::() + TypeDefCompact::new(MetaType::new::()).into() } } diff --git a/src/ty/mod.rs b/src/ty/mod.rs index b465ebf5..2cdf939f 100644 --- a/src/ty/mod.rs +++ b/src/ty/mod.rs @@ -121,8 +121,8 @@ impl From for Type { impl From for Type { fn from(compact: TypeDefCompact) -> Self { Self::new( - // TODO: should the path be empty? Doesn't seem to be used anywhere. - Path::from_segments(vec!["what", "goes", "here"]).expect("TODO proof here"), + // TODO: put `Compact` in the prelude + Path::new("Compact", "scale"), vec![compact.type_param], compact, ) diff --git a/test_suite/tests/derive.rs b/test_suite/tests/derive.rs index 9ac5dd70..12eb0fc4 100644 --- a/test_suite/tests/derive.rs +++ b/test_suite/tests/derive.rs @@ -219,7 +219,7 @@ fn scale_compact_types_work_in_structs() { .composite( Fields::named() .field_of::("a", "u8") - .field_of::>("b", "u16") + .field_of::>("b", "u16"), ); assert_type!(Dense, dense); @@ -245,6 +245,7 @@ fn wip() { ); assert_type!(A, ty); } + #[test] fn scale_compact_types_work_in_enums() { #[allow(unused)] @@ -261,10 +262,7 @@ fn scale_compact_types_work_in_enums() { .variant( Variants::with_fields() .variant("Id", Fields::unnamed().field_of::("AccountId")) - .variant( - "Index", - Fields::unnamed().compact_of::("AccountIndex"), - ) + .variant("Index", Fields::unnamed().compact_of::("AccountIndex")) .variant( "Address32", Fields::unnamed().field_of::<[u8; 32]>("[u8; 32]"), diff --git a/test_suite/tests/json.rs b/test_suite/tests/json.rs index 16188171..19ca871b 100644 --- a/test_suite/tests/json.rs +++ b/test_suite/tests/json.rs @@ -27,6 +27,7 @@ use pretty_assertions::{ assert_eq, assert_ne, }; +use scale::Encode; use scale_info::{ form::PortableForm, meta_type, @@ -206,6 +207,7 @@ fn test_struct_with_some_fields_marked_as_compact() { struct Dense { #[codec(compact)] a: u128, + a_not_compact: u128, b: [u8; 32], #[codec(compact)] c: u64, @@ -222,21 +224,22 @@ fn test_struct_with_some_fields_marked_as_compact() { .path(Path::new("Dense", module_path!())) .composite( Fields::named() - .compact_of::("a", "i32") + .compact_of::("a", "u128") + .field_of::("a_not_compact", "u128") .field_of::<[u8; 32]>("b", "[u8; 32]") - .compact_of::("c", "u64") + .compact_of::("c", "u64"), ) } } - // TODO: this is the correct JSON imo, but this fails. assert_json_for_type::(json![{ - "path": ["json", "Dense"], + "path": ["json", "Dense"], "def": { "composite": { "fields": [ - { "name": "a", "type": 1, "typeName": "i32", "compact": true }, - { "name": "b", "type": 2, "typeName": "[u8; 32]" }, - { "name": "c", "type": 3, "typeName": "u64", "compact": true }, + { "name": "a", "type": 1, "typeName": "u128" }, + { "name": "a_not_compact", "type": 2, "typeName": "u128" }, + { "name": "b", "type": 3, "typeName": "[u8; 32]" }, + { "name": "c", "type": 5, "typeName": "u64" }, ], }, } @@ -347,6 +350,65 @@ fn test_recursive_type_with_box() { assert_eq!(serde_json::to_value(registry).unwrap(), expected_json,); } +#[test] +fn registry_knows_about_compact_types() { + #[allow(unused)] + #[derive(TypeInfo, Encode)] + struct Dense { + #[codec(compact)] + a: u128, + a_not_compact: u128, + b: [u8; 32], + #[codec(compact)] + c: u64, + } + let mut registry = Registry::new(); + let type_id = registry.register_type(&meta_type::()); + + let expected_json = json!({ + "types": [ + { // type 1 + "path": ["json", "Dense"], + "def": { + "composite": { + "fields": [ + { "name": "a", "type": 2, "typeName": "u128" }, + { "name": "a_not_compact", "type": 3, "typeName": "u128" }, + { "name": "b", "type": 4, "typeName": "[u8; 32]" }, + { "name": "c", "type": 6, "typeName": "u64" } + ] + } + } + }, + { // type 2, the `Compact` of field `a`. + "path": ["scale", "Compact"], + "def": { "compact": { "type": 3 } }, + "params": [3] + }, + { // type 3, the `u128` used by type 2 and field `a_not_compact`. + "def": { "primitive": "u128" } + }, + { // type 4, the `[u8; 32]` of field `b`. + "def": { "array": { "len": 32, "type": 5 }} + }, + { // type 5, the `u8` in `[u8; 32]` + "def": { "primitive": "u8" } + }, + { // type 6, the `Compact` of field `c` + "path": ["scale", "Compact"], + "def": { "compact": { "type": 7 } }, + "params": [7] + }, + { // type 7, the `u64` in `Compact` of field `c` + "def": { "primitive": "u64" } + }, + ] + }); + + let registry: PortableRegistry = registry.into(); + assert_eq!(serde_json::to_value(registry).unwrap(), expected_json,); +} + #[test] fn test_registry() { let mut registry = Registry::new(); From e2397bfb6270e7eb446cdc76f80dedb89e2c355c Mon Sep 17 00:00:00 2001 From: David Palm Date: Mon, 11 Jan 2021 13:20:37 +0100 Subject: [PATCH 16/28] Clean up conditionals a bit --- derive/src/lib.rs | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/derive/src/lib.rs b/derive/src/lib.rs index f156b7dd..05b68b11 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -111,27 +111,15 @@ fn generate_fields(fields: &FieldsList) -> Vec { .map(|f| { let (ty, ident) = (&f.ty, &f.ident); let type_name = clean_type_string("e!(#ty).to_string()); - // TODO: make this prettier - if let Some(i) = ident { - if is_compact(f) { - quote! { - .compact_of::<#ty>(stringify!(#i), #type_name) - } - } else { - quote! { - .field_of::<#ty>(stringify!(#i), #type_name) - } - } + let method_call = if is_compact(f) { + quote!(.compact_of::<#ty>) } else { - if is_compact(f) { - quote! { - .compact_of::<#ty>(#type_name) - } - } else { - quote! { - .field_of::<#ty>(#type_name) - } - } + quote!(.field_of::<#ty>) + }; + if let Some(ident) = ident { + quote!(#method_call(stringify!(#ident), #type_name)) + } else { + quote!(#method_call(#type_name)) } }) .collect() From d15bf25d738a997c59f7cc368e7b4f71746993dc Mon Sep 17 00:00:00 2001 From: David Palm Date: Mon, 11 Jan 2021 13:20:58 +0100 Subject: [PATCH 17/28] Add Compact to prelude --- src/prelude.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/prelude.rs b/src/prelude.rs index 0e8c4fe5..c4a3d429 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -57,3 +57,5 @@ cfg_if! { }; } } + +pub use scale::Compact; From 1f4f8d38959e01a391dd4d5103fe1908cef6bdb0 Mon Sep 17 00:00:00 2001 From: David Palm Date: Mon, 11 Jan 2021 13:21:40 +0100 Subject: [PATCH 18/28] Cleanup --- src/meta_type.rs | 1 - src/tests.rs | 7 ++---- src/ty/fields.rs | 3 --- src/ty/mod.rs | 3 +-- test_suite/tests/derive.rs | 44 +++++++++++++++++--------------------- test_suite/tests/json.rs | 4 ++-- 6 files changed, 25 insertions(+), 37 deletions(-) diff --git a/src/meta_type.rs b/src/meta_type.rs index 22566857..b5fe5cfd 100644 --- a/src/meta_type.rs +++ b/src/meta_type.rs @@ -96,7 +96,6 @@ impl MetaType { } } - // TODO: is this used anywhere? /// Creates a new meta type from the type of the given reference. pub fn of(_elem: &T) -> Self where diff --git a/src/tests.rs b/src/tests.rs index 2f97db2e..4b10151d 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -81,14 +81,11 @@ fn prelude_items() { .type_params(tuple_meta_type!(i32)) .composite(Fields::unit()) ); -} -#[test] -fn compact_types() { assert_type!( - scale::Compact, + prelude::Compact, Type::builder() - .path(Path::from_segments(vec!["scale", "Compact"]).unwrap()) + .path(Path::prelude("Compact")) .type_params(tuple_meta_type!(i32)) .compact::() ); diff --git a/src/ty/fields.rs b/src/ty/fields.rs index b360ce7a..e5bd57e8 100644 --- a/src/ty/fields.rs +++ b/src/ty/fields.rs @@ -143,9 +143,6 @@ impl Field { where T: TypeInfo + 'static, { - // TODO: using `scale::Compact` here like this makes the `impl - // TypeInfo for Compact` kick in, but not sure that is the right - // thing to do. :/ Self::new(name, MetaType::new::>(), type_name) } } diff --git a/src/ty/mod.rs b/src/ty/mod.rs index 2cdf939f..41f94790 100644 --- a/src/ty/mod.rs +++ b/src/ty/mod.rs @@ -121,8 +121,7 @@ impl From for Type { impl From for Type { fn from(compact: TypeDefCompact) -> Self { Self::new( - // TODO: put `Compact` in the prelude - Path::new("Compact", "scale"), + Path::prelude("Compact"), vec![compact.type_param], compact, ) diff --git a/test_suite/tests/derive.rs b/test_suite/tests/derive.rs index 12eb0fc4..043b9617 100644 --- a/test_suite/tests/derive.rs +++ b/test_suite/tests/derive.rs @@ -17,7 +17,10 @@ use pretty_assertions::assert_eq; use scale::Encode; use scale_info::{ build::*, - prelude::boxed::Box, + prelude::{ + boxed::Box, + Compact, + }, tuple_meta_type, Path, Type, @@ -214,36 +217,29 @@ fn scale_compact_types_work_in_structs() { b: u16, } - let dense = Type::builder() + let ty = Type::builder() .path(Path::new("Dense", "derive")) .composite( Fields::named() .field_of::("a", "u8") - .field_of::>("b", "u16"), + .field_of::>("b", "u16"), ); - assert_type!(Dense, dense); -} + assert_type!(Dense, ty); -// TODO: both this test and the one above pass, which means something's not -// right. -#[test] -fn wip() { - #[allow(unused)] - #[derive(Encode, TypeInfo)] - struct A { - a: u8, - #[codec(compact)] - b: u16, - } - let ty = Type::builder() - .path(Path::new("A", "derive")) - .composite( - Fields::named() - .field_of::("a", "u8") - .compact_of::("b", "u16") - ); - assert_type!(A, ty); + // TODO: These two tests both pass. This illustrates a doubt I have that + // this is the right way to go. With this PR we take `#[codec(compact)]` to + // mean that the type is encoded/decoded as `Compact` but in reality the + // output of the `Encode` macro is something quite different and not + // `Compact`. + // Maybe this is fine, maybe it's not, just not sure. + + let ty_alt = Type::builder().path(Path::new("Dense", "derive")).composite( + Fields::named() + .field_of::("a", "u8") + .compact_of::("b", "u16"), + ); + assert_type!(Dense, ty_alt); } #[test] diff --git a/test_suite/tests/json.rs b/test_suite/tests/json.rs index 19ca871b..a91e2684 100644 --- a/test_suite/tests/json.rs +++ b/test_suite/tests/json.rs @@ -381,7 +381,7 @@ fn registry_knows_about_compact_types() { } }, { // type 2, the `Compact` of field `a`. - "path": ["scale", "Compact"], + "path": ["Compact"], "def": { "compact": { "type": 3 } }, "params": [3] }, @@ -395,7 +395,7 @@ fn registry_knows_about_compact_types() { "def": { "primitive": "u8" } }, { // type 6, the `Compact` of field `c` - "path": ["scale", "Compact"], + "path": ["Compact"], "def": { "compact": { "type": 7 } }, "params": [7] }, From 02ed1a32e2f6ee72789f56662a0356ea8fcc7f69 Mon Sep 17 00:00:00 2001 From: David Palm Date: Mon, 11 Jan 2021 13:22:14 +0100 Subject: [PATCH 19/28] fmt --- src/ty/mod.rs | 6 +----- test_suite/tests/derive.rs | 12 +++++++----- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/ty/mod.rs b/src/ty/mod.rs index 41f94790..df6897d3 100644 --- a/src/ty/mod.rs +++ b/src/ty/mod.rs @@ -120,11 +120,7 @@ impl From for Type { impl From for Type { fn from(compact: TypeDefCompact) -> Self { - Self::new( - Path::prelude("Compact"), - vec![compact.type_param], - compact, - ) + Self::new(Path::prelude("Compact"), vec![compact.type_param], compact) } } diff --git a/test_suite/tests/derive.rs b/test_suite/tests/derive.rs index 043b9617..dc005c82 100644 --- a/test_suite/tests/derive.rs +++ b/test_suite/tests/derive.rs @@ -234,11 +234,13 @@ fn scale_compact_types_work_in_structs() { // `Compact`. // Maybe this is fine, maybe it's not, just not sure. - let ty_alt = Type::builder().path(Path::new("Dense", "derive")).composite( - Fields::named() - .field_of::("a", "u8") - .compact_of::("b", "u16"), - ); + let ty_alt = Type::builder() + .path(Path::new("Dense", "derive")) + .composite( + Fields::named() + .field_of::("a", "u8") + .compact_of::("b", "u16"), + ); assert_type!(Dense, ty_alt); } From 73892971e5e551ce363f189a42102ab3009be8d8 Mon Sep 17 00:00:00 2001 From: David Palm Date: Mon, 18 Jan 2021 15:03:03 +0100 Subject: [PATCH 20/28] fmt --- src/ty/mod.rs | 8 ++++---- test_suite/tests/derive.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ty/mod.rs b/src/ty/mod.rs index 9b7eeeaa..df697090 100644 --- a/src/ty/mod.rs +++ b/src/ty/mod.rs @@ -422,9 +422,9 @@ where #[cfg_attr( feature = "serde", serde(bound( - serialize = "T::Type: Serialize", - deserialize = "T::Type: DeserializeOwned", -)) + serialize = "T::Type: Serialize", + deserialize = "T::Type: DeserializeOwned", + )) )] pub struct TypeDefCompact { /// The type wrapped in [`Compact`], i.e. the `T` in `Compact`. @@ -437,7 +437,7 @@ impl IntoPortable for TypeDefCompact { fn into_portable(self, registry: &mut Registry) -> Self::Output { TypeDefCompact { - type_param: registry.register_type(&self.type_param), + type_param: registry.register_type(&self.type_param), } } } diff --git a/test_suite/tests/derive.rs b/test_suite/tests/derive.rs index 3012d9f9..0b080774 100644 --- a/test_suite/tests/derive.rs +++ b/test_suite/tests/derive.rs @@ -20,8 +20,8 @@ use scale_info::{ build::*, prelude::{ boxed::Box, - Compact, marker::PhantomData, + Compact, }, tuple_meta_type, Path, From d8506dc7721e50c777248da51cef49da4e9376a6 Mon Sep 17 00:00:00 2001 From: David Palm Date: Mon, 18 Jan 2021 15:42:50 +0100 Subject: [PATCH 21/28] Sort out TODOs Add a macro to cleanup boilerplate --- src/tests.rs | 10 ++----- src/ty/mod.rs | 59 ++++++++++++++-------------------------- test_suite/tests/json.rs | 4 --- 3 files changed, 22 insertions(+), 51 deletions(-) diff --git a/src/tests.rs b/src/tests.rs index 11aa8275..7a563542 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -14,6 +14,7 @@ use crate::{ build::*, + prelude::Compact, *, }; use core::marker::PhantomData; @@ -75,14 +76,7 @@ fn prelude_items() { ) ); assert_type!(PhantomData, TypeDefPhantom::new(meta_type::())); - // TODO: this should look like the rest of them - assert_type!( - prelude::Compact, - Type::builder() - .path(Path::prelude("Compact")) - .type_params(tuple_meta_type!(i32)) - .compact::() - ); + assert_type!(Compact, TypeDefCompact::new(meta_type::())) } #[test] diff --git a/src/ty/mod.rs b/src/ty/mod.rs index df697090..b1d2ed2d 100644 --- a/src/ty/mod.rs +++ b/src/ty/mod.rs @@ -94,43 +94,24 @@ impl IntoPortable for Type { } } -impl From for Type { - fn from(primitive: TypeDefPrimitive) -> Self { - Self::new(Path::voldemort(), Vec::new(), primitive) - } -} - -impl From for Type { - fn from(array: TypeDefArray) -> Self { - Self::new(Path::voldemort(), Vec::new(), array) - } -} - -impl From for Type { - fn from(sequence: TypeDefSequence) -> Self { - Self::new(Path::voldemort(), Vec::new(), sequence) - } -} - -impl From for Type { - fn from(tuple: TypeDefTuple) -> Self { - Self::new(Path::voldemort(), Vec::new(), tuple) - } -} - -impl From for Type { - fn from(compact: TypeDefCompact) -> Self { - // TODO: should be like the others - // TODO: write macro for these - Self::new(Path::prelude("Compact"), vec![compact.type_param], compact) - } +macro_rules! impl_from_type_def_for_type { + ( $( $t:ty ), + $(,)?) => { $( + impl From<$t> for Type { + fn from(item: $t) -> Self { + Self::new(Path::voldemort(), Vec::new(), item) + } + } + )* } } -impl From for Type { - fn from(phantom: TypeDefPhantom) -> Self { - Self::new(Path::voldemort(), Vec::new(), phantom) - } -} +impl_from_type_def_for_type!( + TypeDefPrimitive, + TypeDefArray, + TypeDefSequence, + TypeDefTuple, + TypeDefCompact, + TypeDefPhantom, +); impl Type { /// Create a [`TypeBuilder`](`crate::build::TypeBuilder`) the public API for constructing a [`Type`] @@ -422,6 +403,7 @@ where #[cfg_attr( feature = "serde", serde(bound( + // TODO: check these bounds serialize = "T::Type: Serialize", deserialize = "T::Type: DeserializeOwned", )) @@ -468,16 +450,15 @@ where /// Instead we take the same approach as `parity-scale-codec` where users are /// required to explicitly skip fields that cannot be represented in SCALE /// encoding, using the `#[codec(skip)]` attribute. -#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr( feature = "serde", - // TODO: check these bounds serde(bound( - serialize = "T::Type: Serialize, T::String: Serialize", - deserialize = "T::Type: DeserializeOwned, T::String: DeserializeOwned", + serialize = "T::Type: Serialize", + deserialize = "T::Type: DeserializeOwned", )) )] +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, Debug)] pub struct TypeDefPhantom { /// The PhantomData type parameter #[cfg_attr(feature = "serde", serde(rename = "type"))] diff --git a/test_suite/tests/json.rs b/test_suite/tests/json.rs index d7fc8e7d..393a670e 100644 --- a/test_suite/tests/json.rs +++ b/test_suite/tests/json.rs @@ -401,9 +401,7 @@ fn registry_knows_about_compact_types() { } }, { // type 2, the `Compact` of field `a`. - "path": ["Compact"], "def": { "compact": { "type": 3 } }, - "params": [3] }, { // type 3, the `u128` used by type 2 and field `a_not_compact`. "def": { "primitive": "u128" } @@ -415,9 +413,7 @@ fn registry_knows_about_compact_types() { "def": { "primitive": "u8" } }, { // type 6, the `Compact` of field `c` - "path": ["Compact"], "def": { "compact": { "type": 7 } }, - "params": [7] }, { // type 7, the `u64` in `Compact` of field `c` "def": { "primitive": "u64" } From ab561180435ea7c06dc2a367fdda58f935afed9f Mon Sep 17 00:00:00 2001 From: David Palm Date: Tue, 19 Jan 2021 15:45:47 +0100 Subject: [PATCH 22/28] Remove unused top-level way of adding a Compact --- src/build.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/build.rs b/src/build.rs index f37c991f..9669aeed 100644 --- a/src/build.rs +++ b/src/build.rs @@ -128,7 +128,6 @@ use crate::{ Path, Type, TypeDef, - TypeDefCompact, TypeDefComposite, TypeDefVariant, TypeInfo, @@ -189,11 +188,6 @@ impl TypeBuilder { pub fn composite(self, fields: FieldsBuilder) -> Type { self.build(TypeDefComposite::new(fields.finalize())) } - - /// Construct a [`Compact`] type, i.e. a `scale::Compact` - pub fn compact(self) -> Type { - self.build(TypeDefCompact::new(MetaType::new::())) - } } impl TypeBuilder { From 1c0c05d8a499ffc40ea264db9a7473c35c39323f Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 29 Jan 2021 13:32:10 +0100 Subject: [PATCH 23/28] Remove unneeded bound --- src/ty/mod.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/ty/mod.rs b/src/ty/mod.rs index 25f71719..91429073 100644 --- a/src/ty/mod.rs +++ b/src/ty/mod.rs @@ -386,14 +386,6 @@ where /// A type wrapped in [`Compact`]. #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[cfg_attr( - feature = "serde", - serde(bound( - // TODO: check these bounds - serialize = "T::Type: Serialize", - deserialize = "T::Type: DeserializeOwned", - )) -)] pub struct TypeDefCompact { /// The type wrapped in [`Compact`], i.e. the `T` in `Compact`. #[cfg_attr(feature = "serde", serde(rename = "type"))] From 693b23af27283ced6eb845a5d10232074ee25eab Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 29 Jan 2021 14:40:23 +0100 Subject: [PATCH 24/28] Remove `Compact` from the prelude --- src/prelude.rs | 2 -- src/tests.rs | 2 +- test_suite/tests/derive.rs | 3 +-- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/prelude.rs b/src/prelude.rs index c4a3d429..0e8c4fe5 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -57,5 +57,3 @@ cfg_if! { }; } } - -pub use scale::Compact; diff --git a/src/tests.rs b/src/tests.rs index 7a563542..c4174026 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -14,10 +14,10 @@ use crate::{ build::*, - prelude::Compact, *, }; use core::marker::PhantomData; +use scale::Compact; #[cfg(not(feature = "std"))] use alloc::{ diff --git a/test_suite/tests/derive.rs b/test_suite/tests/derive.rs index 0b080774..ede071c3 100644 --- a/test_suite/tests/derive.rs +++ b/test_suite/tests/derive.rs @@ -15,13 +15,12 @@ #![cfg_attr(not(feature = "std"), no_std)] use pretty_assertions::assert_eq; -use scale::Encode; +use scale::{Compact, Encode}; use scale_info::{ build::*, prelude::{ boxed::Box, marker::PhantomData, - Compact, }, tuple_meta_type, Path, From c2a38f54d003731d43d3e16b3da54cca69fa2f18 Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 29 Jan 2021 14:41:30 +0100 Subject: [PATCH 25/28] Resolve TODO --- test_suite/tests/derive.rs | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/test_suite/tests/derive.rs b/test_suite/tests/derive.rs index ede071c3..919f050d 100644 --- a/test_suite/tests/derive.rs +++ b/test_suite/tests/derive.rs @@ -244,23 +244,6 @@ fn scale_compact_types_work_in_structs() { b: u16, } - let ty = Type::builder() - .path(Path::new("Dense", "derive")) - .composite( - Fields::named() - .field_of::("a", "u8") - .field_of::>("b", "u16"), - ); - - assert_type!(Dense, ty); - - // TODO: These two tests both pass. This illustrates a doubt I have that - // this is the right way to go. With this PR we take `#[codec(compact)]` to - // mean that the type is encoded/decoded as `Compact` but in reality the - // output of the `Encode` macro is something quite different and not - // `Compact`. - // Maybe this is fine, maybe it's not, just not sure. - let ty_alt = Type::builder() .path(Path::new("Dense", "derive")) .composite( From 5649a666bb3e8228ebdf59e5844ddf22604594a5 Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 29 Jan 2021 14:41:46 +0100 Subject: [PATCH 26/28] fmt --- test_suite/tests/derive.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test_suite/tests/derive.rs b/test_suite/tests/derive.rs index 919f050d..4e5016ed 100644 --- a/test_suite/tests/derive.rs +++ b/test_suite/tests/derive.rs @@ -15,7 +15,10 @@ #![cfg_attr(not(feature = "std"), no_std)] use pretty_assertions::assert_eq; -use scale::{Compact, Encode}; +use scale::{ + Compact, + Encode, +}; use scale_info::{ build::*, prelude::{ From aad32771bc4b5d8aa853de5dd2fb5c135781367d Mon Sep 17 00:00:00 2001 From: David Palm Date: Fri, 29 Jan 2021 20:01:56 +0100 Subject: [PATCH 27/28] Use ::Type rather than Compact (ty @thiolliere!) --- derive/src/trait_bounds.rs | 28 +++++++++++++++++++--------- src/build.rs | 6 ++++-- src/tests.rs | 4 ++++ src/ty/fields.rs | 6 ++++-- 4 files changed, 31 insertions(+), 13 deletions(-) diff --git a/derive/src/trait_bounds.rs b/derive/src/trait_bounds.rs index 7ee5b23e..f5eafa09 100644 --- a/derive/src/trait_bounds.rs +++ b/derive/src/trait_bounds.rs @@ -51,10 +51,20 @@ pub fn make_where_clause<'a>( let types = collect_types_to_bind(input_ident, data, &ty_params_ids)?; - types.into_iter().for_each(|ty| { - where_clause - .predicates - .push(parse_quote!(#ty : ::scale_info::TypeInfo + 'static)) + types.into_iter().for_each(|(ty, is_compact)| { + // Compact types need extra bounds, T: HasCompact and ::Type: TypeInfo + 'static + if is_compact { + where_clause + .predicates + .push(parse_quote!(#ty : ::scale::HasCompact)); + where_clause + .predicates + .push(parse_quote!(<#ty as ::scale::HasCompact>::Type : ::scale_info::TypeInfo + 'static)); + } else { + where_clause + .predicates + .push(parse_quote!(#ty : ::scale_info::TypeInfo + 'static)); + } }); generics.type_params().into_iter().for_each(|type_param| { @@ -94,14 +104,14 @@ fn type_contains_idents(ty: &Type, idents: &[Ident]) -> bool { visitor.result } -/// Returns all types that must be added to the where clause with the respective -/// trait bound. +/// Returns all types that must be added to the where clause with a boolean +/// indicating if the field is [`scale::Compact`] or not. fn collect_types_to_bind( input_ident: &Ident, data: &syn::Data, ty_params: &[Ident], -) -> Result> { - let types_from_fields = |fields: &Punctuated| -> Vec { +) -> Result> { + let types_from_fields = |fields: &Punctuated| -> Vec<(Type, bool)> { fields .iter() .filter(|field| { @@ -112,7 +122,7 @@ fn collect_types_to_bind( // to not have them in the where clause. !type_contains_idents(&field.ty, &[input_ident.clone()]) }) - .map(|f| f.ty.clone()) + .map(|f| (f.ty.clone(), super::is_compact(f))) .collect() }; diff --git a/src/build.rs b/src/build.rs index 9669aeed..3bad9718 100644 --- a/src/build.rs +++ b/src/build.rs @@ -263,7 +263,8 @@ impl FieldsBuilder { /// Add a named, [`Compact`] field of type `T`. pub fn compact_of(mut self, name: &'static str, type_name: &'static str) -> Self where - T: TypeInfo + 'static, + T: scale::HasCompact, + ::Type: TypeInfo + 'static, { self.fields .push(Field::compact_of::(Some(name), type_name)); @@ -284,7 +285,8 @@ impl FieldsBuilder { /// Add an unnamed, [`Compact`] field of type `T`. pub fn compact_of(mut self, type_name: &'static str) -> Self where - T: TypeInfo + 'static, + T: scale::HasCompact, + ::Type: TypeInfo + 'static, { self.fields.push(Field::compact_of::(None, type_name)); self diff --git a/src/tests.rs b/src/tests.rs index c4174026..fbbf4bea 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -76,6 +76,10 @@ fn prelude_items() { ) ); assert_type!(PhantomData, TypeDefPhantom::new(meta_type::())); +} + +#[test] +fn scale_compact_types() { assert_type!(Compact, TypeDefCompact::new(meta_type::())) } diff --git a/src/ty/fields.rs b/src/ty/fields.rs index 9229ab9b..2b709289 100644 --- a/src/ty/fields.rs +++ b/src/ty/fields.rs @@ -26,6 +26,7 @@ use crate::{ use scale::{ Decode, Encode, + HasCompact, }; #[cfg(feature = "serde")] use serde::{ @@ -133,9 +134,10 @@ impl Field { /// Creates a new [`Compact`] field. pub fn compact_of(name: Option<&'static str>, type_name: &'static str) -> Field where - T: TypeInfo + 'static, + T: HasCompact, + ::Type: TypeInfo + 'static, { - Self::new(name, MetaType::new::>(), type_name) + Self::new(name, MetaType::new::<::Type>(), type_name) } } From 4b185000a9e7ec8cb35d9324afbb35d30da29ca2 Mon Sep 17 00:00:00 2001 From: David Palm Date: Sun, 31 Jan 2021 21:59:48 +0100 Subject: [PATCH 28/28] Split long comment --- derive/src/trait_bounds.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/derive/src/trait_bounds.rs b/derive/src/trait_bounds.rs index f5eafa09..34228ed3 100644 --- a/derive/src/trait_bounds.rs +++ b/derive/src/trait_bounds.rs @@ -52,7 +52,8 @@ pub fn make_where_clause<'a>( let types = collect_types_to_bind(input_ident, data, &ty_params_ids)?; types.into_iter().for_each(|(ty, is_compact)| { - // Compact types need extra bounds, T: HasCompact and ::Type: TypeInfo + 'static + // Compact types need extra bounds, T: HasCompact and ::Type: TypeInfo + 'static if is_compact { where_clause .predicates