From 37c0a577ccd9973ee246b37a558f1b3b2c225391 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 7 Dec 2020 11:48:59 +0000 Subject: [PATCH 01/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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() {