Skip to content
This repository has been archived by the owner on Jan 4, 2023. It is now read-only.

Introduced two new traits for spec's serialization and deserialization #190

Draft
wants to merge 14 commits into
base: master
Choose a base branch
from
Draft
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ fuel-asm = { version = "0.9", default-features = false }
fuel-crypto = { version = "0.6", default-features = false }
fuel-merkle = { version = "0.4", default-features = false }
fuel-types = { version = "0.5", default-features = false }
fuel-tx-derive = { path = "derive" }
itertools = { version = "0.10", default-features = false }
rand = { version = "0.8", default-features = false, features = ["std_rng"], optional = true }
num-integer = { version = "0.1", default-features = false }
Expand All @@ -28,6 +29,7 @@ fuel-tx = { path = ".", features = ["builder", "random"] }
fuel-tx-test-helpers = { path = "test-helpers" }
fuel-types = { version = "0.5", default-features = false, features = ["random"] }
insta = "1.0"
hex = "0.4"
quickcheck = "1.0"
quickcheck_macros = "1.0"
rand = { version = "0.8", default-features = false, features = ["std_rng"] }
Expand Down
21 changes: 21 additions & 0 deletions derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[package]
name = "fuel-tx-derive"
version = "0.20.0"
authors = ["Fuel Labs <contact@fuel.sh>"]
categories = ["cryptography::cryptocurrencies", "data-structures"]
edition = "2021"
homepage = "https://fuel.network/"
keywords = ["blockchain", "cryptocurrencies", "fuel-vm", "vm"]
license = "Apache-2.0"
repository = "https://github.com/FuelLabs/fuel-tx"
description = "FuelVM derive for `fuel-tx`."

[lib]
proc-macro = true

[dependencies]
quote = "1"
syn = { version = "1", features = ["full"] }
proc-macro2 = "1"
synstructure = "0.12.4"

110 changes: 110 additions & 0 deletions derive/src/deserialize.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;

fn deserialize_struct(s: &synstructure::Structure) -> TokenStream2 {
assert_eq!(s.variants().len(), 1, "structs must have one variant");

let variant: &synstructure::VariantInfo = &s.variants()[0];
let decode_main = variant.construct(|field, _| {
let ty = &field.ty;
quote! {
<#ty as fuel_tx::canonical::Deserialize>::decode_static(buffer)?
}
});

let decode_dynamic = variant.each(|binding| {
quote! {
fuel_tx::canonical::Deserialize::decode_dynamic(#binding, buffer)?;
}
});

s.gen_impl(quote! {
gen impl fuel_tx::canonical::Deserialize for @Self {
fn decode_static<I: fuel_tx::canonical::Input + ?Sized>(buffer: &mut I) -> ::core::result::Result<Self, fuel_tx::canonical::Error> {
::core::result::Result::Ok(#decode_main)
}

fn decode_dynamic<I: fuel_tx::canonical::Input + ?Sized>(&mut self, buffer: &mut I) -> ::core::result::Result<(), fuel_tx::canonical::Error> {
match self {
#decode_dynamic,
};
::core::result::Result::Ok(())
}
}
})
}

fn deserialize_enum(s: &synstructure::Structure) -> TokenStream2 {
assert!(!s.variants().is_empty(), "got invalid empty enum");
let decode_static = s
.variants()
.iter()
.map(|variant| {
let decode_main = variant.construct(|field, _| {
let ty = &field.ty;
quote! {
<#ty as fuel_tx::canonical::Deserialize>::decode_static(buffer)?
}
});

quote! {
{
::core::result::Result::Ok(#decode_main)
}
}
})
.enumerate()
.fold(quote! {}, |acc, (i, v)| {
let index = i as u64;
quote! {
#acc
#index => #v,
}
});

let decode_dynamic = s.variants().iter().map(|variant| {
let decode_dynamic = variant.each(|binding| {
quote! {
fuel_tx::canonical::Deserialize::decode_dynamic(#binding, buffer)?;
}
});

quote! {
#decode_dynamic
}
});

s.gen_impl(quote! {
gen impl fuel_tx::canonical::Deserialize for @Self {
fn decode_static<I: fuel_tx::canonical::Input + ?Sized>(buffer: &mut I) -> ::core::result::Result<Self, fuel_tx::canonical::Error> {
match <::core::primitive::u64 as fuel_tx::canonical::Deserialize>::decode(buffer)? {
#decode_static
_ => return ::core::result::Result::Err(fuel_tx::canonical::Error::UnknownDiscriminant),
}
}

fn decode_dynamic<I: fuel_tx::canonical::Input + ?Sized>(&mut self, buffer: &mut I) -> ::core::result::Result<(), fuel_tx::canonical::Error> {
match self {
#(
#decode_dynamic
)*
_ => return ::core::result::Result::Err(fuel_tx::canonical::Error::UnknownDiscriminant),
};

::core::result::Result::Ok(())
}
}
})
}

/// Derives `Deserialize` trait for the given `struct` or `enum`.
pub fn deserialize_derive(mut s: synstructure::Structure) -> TokenStream2 {
s.bind_with(|_| synstructure::BindStyle::RefMut)
.add_bounds(synstructure::AddBounds::Fields)
.underscore_const(true);
match s.ast().data {
syn::Data::Struct(_) => deserialize_struct(&s),
syn::Data::Enum(_) => deserialize_enum(&s),
_ => panic!("Can't derive `Deserialize` for `union`s"),
}
}
16 changes: 16 additions & 0 deletions derive/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
extern crate proc_macro;

mod deserialize;
mod serialize;

use self::{deserialize::deserialize_derive, serialize::serialize_derive};
synstructure::decl_derive!(
[Deserialize] =>
/// Derives `Deserialize` trait for the given `struct` or `enum`.
deserialize_derive
);
synstructure::decl_derive!(
[Serialize] =>
/// Derives `Serialize` trait for the given `struct` or `enum`.
serialize_derive
);
114 changes: 114 additions & 0 deletions derive/src/serialize.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;

fn serialize_struct(s: &synstructure::Structure) -> TokenStream2 {
assert_eq!(s.variants().len(), 1, "structs must have one variant");

let variant: &synstructure::VariantInfo = &s.variants()[0];
let encode_static = variant.each(|binding| {
quote! {
if fuel_tx::canonical::Serialize::size(#binding) % fuel_tx::canonical::ALIGN > 0 {
return ::core::result::Result::Err(fuel_tx::canonical::Error::WrongAlign)
}
fuel_tx::canonical::Serialize::encode_static(#binding, buffer)?;
}
});

let encode_dynamic = variant.each(|binding| {
quote! {
fuel_tx::canonical::Serialize::encode_dynamic(#binding, buffer)?;
}
});

s.gen_impl(quote! {
gen impl fuel_tx::canonical::Serialize for @Self {
#[inline(always)]
fn encode_static<O: fuel_tx::canonical::Output + ?Sized>(&self, buffer: &mut O) -> ::core::result::Result<(), fuel_tx::canonical::Error> {
match self {
#encode_static
};

::core::result::Result::Ok(())
}

fn encode_dynamic<O: fuel_tx::canonical::Output + ?Sized>(&self, buffer: &mut O) -> ::core::result::Result<(), fuel_tx::canonical::Error> {
match self {
#encode_dynamic
};

::core::result::Result::Ok(())
}
}
})
}

fn serialize_enum(s: &synstructure::Structure) -> TokenStream2 {
assert!(!s.variants().is_empty(), "got invalid empty enum");
let encode_static = s.variants().iter().enumerate().map(|(i, v)| {
let pat = v.pat();
let index = i as u64;
let encode_static_iter = v.bindings().iter().map(|binding| {
quote! {
if fuel_tx::canonical::Serialize::size(#binding) % fuel_tx::canonical::ALIGN > 0 {
return ::core::result::Result::Err(fuel_tx::canonical::Error::WrongAlign)
}
fuel_tx::canonical::Serialize::encode_static(#binding, buffer)?;
}
});
quote! {
#pat => {
{ <::core::primitive::u64 as fuel_tx::canonical::Serialize>::encode(&#index, buffer)?; }
#(
{ #encode_static_iter }
)*
}
}
});
let encode_dynamic = s.variants().iter().map(|v| {
let encode_dynamic_iter = v.each(|binding| {
quote! {
fuel_tx::canonical::Serialize::encode_dynamic(#binding, buffer)?;
}
});
quote! {
#encode_dynamic_iter
}
});
s.gen_impl(quote! {
gen impl fuel_tx::canonical::Serialize for @Self {
#[inline(always)]
fn encode_static<O: fuel_tx::canonical::Output + ?Sized>(&self, buffer: &mut O) -> ::core::result::Result<(), fuel_tx::canonical::Error> {
match self {
#(
#encode_static
)*,
_ => return ::core::result::Result::Err(fuel_tx::canonical::Error::UnknownDiscriminant),
};

::core::result::Result::Ok(())
}

fn encode_dynamic<O: fuel_tx::canonical::Output + ?Sized>(&self, buffer: &mut O) -> ::core::result::Result<(), fuel_tx::canonical::Error> {
match self {
#(
#encode_dynamic
)*,
_ => return ::core::result::Result::Err(fuel_tx::canonical::Error::UnknownDiscriminant),
};

::core::result::Result::Ok(())
}
}
})
}

/// Derives `Serialize` trait for the given `struct` or `enum`.
pub fn serialize_derive(mut s: synstructure::Structure) -> TokenStream2 {
s.add_bounds(synstructure::AddBounds::Fields)
.underscore_const(true);
match s.ast().data {
syn::Data::Struct(_) => serialize_struct(&s),
syn::Data::Enum(_) => serialize_enum(&s),
_ => panic!("Can't derive `Serialize` for `union`s"),
}
}
Loading