Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add a compact flag to Field to indicate that this type is to be e…
…ncoded/decoded as a SCALE Compact type
  • Loading branch information
dvdplm committed Dec 17, 2020
commit 3a95663438b63b721e37d62f89c00fd4b3cf90d4
1 change: 0 additions & 1 deletion derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ fn generate_type(input: TokenStream2) -> Result<TokenStream2> {
.path(::scale_info::Path::new(stringify!(#ident), module_path!()))
.type_params(::scale_info::prelude::vec![ #( #generic_type_ids ),* ])
.#build_type
.into()
}
}
};
Expand Down
9 changes: 9 additions & 0 deletions src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,15 @@ impl<T> FieldsBuilder<T> {
pub fn finalize(self) -> Vec<Field<MetaForm>> {
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<NamedFields> {
Expand Down
1 change: 0 additions & 1 deletion src/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,6 @@ mod tests {
"&mut RecursiveRefs",
),
)
.into()
}
}

Expand Down
1 change: 0 additions & 1 deletion src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<T>("data", "T"))
.into()
}
}

Expand Down
21 changes: 19 additions & 2 deletions src/ty/fields.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ pub struct Field<T: Form = MetaForm> {
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,
Comment on lines +89 to +92
Copy link
Contributor

@Robbepop Robbepop Jan 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think an even more compact representation for the exact same use case would be to use yet another enum variant in order to represent compacted types. The idea is to add another variant Compact(TypeDefCompact<T>) to the TypeDef enum: https://docs.rs/scale-info/0.4.1/scale_info/enum.TypeDef.html
The TypeDefCompact type internally could look like this:

pub struct TypeDefCompact<T>
where
    T: Form = MetaForm
{
    #[serde(rename = "type")]
    type_param: T::Type,
}

Then instead of requiring each type to have this bool field we'd simply have a compact variant only for those type definitions that are actually compactly encoded. Since this is kind of a special case this design would suite much better. Also it would better reflect the encoding structure.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At first glance I think this solution is more elegant than having the field, while still making it a first class citizen (unlike just using a type e.g. Compact<T>.

}

fn is_false(v: &bool) -> bool {
!v
}

impl IntoFrozen for Field {
Expand All @@ -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,
}
}
}
Expand All @@ -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,
}
}

Expand All @@ -124,7 +135,7 @@ impl Field {
where
T: TypeInfo + ?Sized + 'static,
{
Self::new(Some(name), MetaType::new::<T>(), type_name)
Self::new(Some(name), MetaType::new::<T>(), type_name, false)
}

/// Creates a new unnamed field.
Expand All @@ -135,7 +146,7 @@ impl Field {
where
T: TypeInfo + ?Sized + 'static,
{
Self::new(None, MetaType::new::<T>(), type_name)
Self::new(None, MetaType::new::<T>(), type_name, false)
}
}

Expand All @@ -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;
}
}
48 changes: 48 additions & 0 deletions test_suite/tests/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<u8>("a", "i32")
.compact()
.field_of::<[u8; 32]>("b", "[u8; 32]")
.field_of::<u64>("c", "u64")
.compact(),
)
}
}

assert_json_for_type::<Dense>(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)]
Expand Down