Skip to content

Commit

Permalink
feat(gadget-sdk)!: integrate blueprint-serde (#469)
Browse files Browse the repository at this point in the history
  • Loading branch information
Serial-ATA authored Nov 13, 2024
1 parent ae45c67 commit 47edd9a
Show file tree
Hide file tree
Showing 13 changed files with 158 additions and 394 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

53 changes: 34 additions & 19 deletions macros/blueprint-proc-macro-core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::borrow::Cow;

pub type BlueprintString<'a> = std::borrow::Cow<'a, str>;
/// A type that represents an EVM Address.
pub type Address = ethereum_types::H160;
Expand Down Expand Up @@ -52,26 +54,39 @@ pub enum FieldType {
AccountId,
}

impl AsRef<str> for FieldType {
fn as_ref(&self) -> &str {
impl FieldType {
pub fn as_rust_type(&self) -> Cow<'_, str> {
match self {
FieldType::Uint8 => "u8",
FieldType::Uint16 => "u16",
FieldType::Uint32 => "u32",
FieldType::Uint64 => "u64",
FieldType::Int8 => "i8",
FieldType::Int16 => "i16",
FieldType::Int32 => "i32",
FieldType::Int64 => "i64",
FieldType::Uint128 => "u128",
FieldType::U256 => "U256",
FieldType::Int128 => "i128",
FieldType::Float64 => "f64",
FieldType::Bool => "bool",
FieldType::String => "String",
FieldType::Bytes => "Bytes",
FieldType::AccountId => "AccountId",
ty => unimplemented!("Unsupported FieldType {ty:?}"),
FieldType::Uint8 => Cow::Borrowed("u8"),
FieldType::Uint16 => Cow::Borrowed("u16"),
FieldType::Uint32 => Cow::Borrowed("u32"),
FieldType::Uint64 => Cow::Borrowed("u64"),
FieldType::Int8 => Cow::Borrowed("i8"),
FieldType::Int16 => Cow::Borrowed("i16"),
FieldType::Int32 => Cow::Borrowed("i32"),
FieldType::Int64 => Cow::Borrowed("i64"),
FieldType::Uint128 => Cow::Borrowed("u128"),
FieldType::U256 => Cow::Borrowed("U256"),
FieldType::Int128 => Cow::Borrowed("i128"),
FieldType::Float64 => Cow::Borrowed("f64"),
FieldType::Bool => Cow::Borrowed("bool"),
FieldType::String => Cow::Borrowed("String"),
FieldType::Bytes => Cow::Borrowed("Vec<u8>"),
FieldType::AccountId => Cow::Borrowed("AccountId"),

FieldType::Optional(ty) => Cow::Owned(format!("Option<{}>", ty.as_rust_type())),
FieldType::Array(size, ty) => Cow::Owned(format!("[{}; {size}]", ty.as_rust_type())),
FieldType::List(ty) => Cow::Owned(format!("Vec<{}>", ty.as_rust_type())),
FieldType::Struct(..) => unimplemented!("FieldType::Struct encoding"),
FieldType::Tuple(tys) => {
let mut s = String::from("(");
for ty in tys {
s.push_str(&format!("{},", ty.as_rust_type()));
}
s.push(')');
Cow::Owned(s)
}
FieldType::Void => panic!("Void is not a representable type"),
}
}
}
Expand Down
9 changes: 5 additions & 4 deletions macros/blueprint-proc-macro/src/hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,11 @@ fn generate_hook_params(input: &ForeignItemFn) -> syn::Result<String> {

let param_types = crate::shared::param_types(&input.sig)?;

let params = param_types
.values()
.map(crate::shared::type_to_field_type)
.collect::<syn::Result<Vec<_>>>()?;
let mut params = Vec::new();
for param_type in param_types.values() {
let param_type = crate::shared::type_to_field_type(param_type)?;
params.push(param_type.ty);
}

let hook_params = serde_json::to_string(&params).map_err(|err| {
syn::Error::new_spanned(input, format!("failed to serialize hook: {err}"))
Expand Down
73 changes: 52 additions & 21 deletions macros/blueprint-proc-macro/src/job.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ use indexmap::{IndexMap, IndexSet};
use itertools::Itertools;
use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::{format_ident, quote, ToTokens};
use quote::{format_ident, quote, quote_spanned, ToTokens};
use std::collections::HashSet;
use std::str::FromStr;
use syn::ext::IdentExt;
use syn::parse::{Parse, ParseBuffer, ParseStream};
use syn::{Ident, Index, ItemFn, LitInt, Token, Type};
Expand Down Expand Up @@ -152,8 +153,8 @@ impl IsResultType for Type {
/// Creates Job Definition using input parameters
pub fn generate_job_const_block(
input: &ItemFn,
params: Vec<FieldType>,
result: Vec<FieldType>,
params: Vec<ParameterType>,
result: Vec<ParameterType>,
job_id: &LitInt,
) -> syn::Result<proc_macro2::TokenStream> {
let (fn_name_string, job_def_name, job_id_name) = get_job_id_field_name(input);
Expand All @@ -164,8 +165,8 @@ pub fn generate_job_const_block(
// filled later on during the rustdoc gen.
description: None,
},
params,
result,
params: params.iter().map(ParameterType::field_type).collect(),
result: result.iter().map(ParameterType::field_type).collect(),
};

// Serialize Job Definition to JSON string
Expand Down Expand Up @@ -769,20 +770,39 @@ impl Parse for Params {
}
}

#[derive(Debug, Clone)]
pub struct ParameterType {
pub ty: FieldType,
pub span: Option<Span>,
}

impl PartialEq for ParameterType {
fn eq(&self, other: &Self) -> bool {
self.ty == other.ty
}
}

impl Eq for ParameterType {}

impl ParameterType {
pub(crate) fn field_type(&self) -> FieldType {
self.ty.clone()
}
}

pub(crate) fn declared_params_to_field_types(
params: &[Ident],
param_types: &IndexMap<Ident, Type>,
) -> syn::Result<Vec<FieldType>> {
let params = params
.iter()
.map(|ident| {
param_types.get(ident).ok_or_else(|| {
syn::Error::new_spanned(ident, "parameter not declared in the function")
})
})
.map(|ty| type_to_field_type(ty?))
.collect::<syn::Result<Vec<_>>>()?;
Ok(params)
) -> syn::Result<Vec<ParameterType>> {
let mut ret = Vec::new();
for param in params {
let ty = param_types.get(param).ok_or_else(|| {
syn::Error::new_spanned(param, "parameter not declared in the function")
})?;

ret.push(type_to_field_type(ty)?)
}
Ok(ret)
}

pub enum ResultsKind {
Expand Down Expand Up @@ -832,7 +852,7 @@ pub(crate) struct EventListenerArgs {
pub(crate) listeners: Vec<SingleListener>,
}

#[derive(Debug, Eq, PartialEq)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum ListenerType {
Evm,
Tangle,
Expand Down Expand Up @@ -994,17 +1014,28 @@ impl EventListenerArgs {

pub fn get_param_name_tokenstream(
&self,
params: &[FieldType],
params: &[ParameterType],
) -> Vec<proc_macro2::TokenStream> {
let listener_type = self.get_event_listener().listener_type;

params
.iter()
.enumerate()
.map(|(i, t)| {
.map(|(i, param_ty)| {
let ident = format_ident!("param{i}");
let index = Index::from(i);
match self.get_event_listener().listener_type {
match listener_type {
ListenerType::Tangle => {
crate::special_impls::tangle::field_type_to_param_token(&ident, t)
let ty_token_stream = proc_macro2::TokenStream::from_str(&param_ty.ty.as_rust_type()).expect("should be valid");
let ty_tokens = quote_spanned! {param_ty.span.expect("should always be available")=>
#ty_token_stream
};
quote! {
let __arg = args.next().expect("parameter count checked before");
let Ok(#ident) = ::gadget_sdk::ext::blueprint_serde::from_field::<#ty_tokens>(__arg) else {
return Err(::gadget_sdk::Error::BadArgumentDecoding(format!("Failed to decode the field `{}` to `{}`", stringify!(#ident), stringify!(#ty_tokens))));
};
}
}
ListenerType::Evm => {
quote! {
Expand Down
6 changes: 3 additions & 3 deletions macros/blueprint-proc-macro/src/report.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::job::{
declared_params_to_field_types, generate_autogen_struct, generate_specialized_logic,
get_current_call_id_field_name, get_job_id_field_name, get_return_type, EventListenerArgs,
ResultsKind,
ParameterType, ResultsKind,
};
use crate::shared::{pascal_case, MacroExt};
use gadget_blueprint_proc_macro_core::{
Expand Down Expand Up @@ -76,8 +76,8 @@ pub(crate) fn report_impl(args: &ReportArgs, input: &ItemFn) -> syn::Result<Toke
name: fn_name_string.clone().into(),
description: None, // This will get auto-injected during the build process
},
params: params_type.clone(),
result: result_type.clone(),
params: params_type.iter().map(ParameterType::field_type).collect(),
result: result_type.iter().map(ParameterType::field_type).collect(),
report_type: args.report_type.clone(),
job_id,
interval: args
Expand Down
46 changes: 29 additions & 17 deletions macros/blueprint-proc-macro/src/shared.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use crate::job::ParameterType;
use crate::job::{IsResultType, ResultsKind};
use gadget_blueprint_proc_macro_core::FieldType;
use indexmap::IndexMap;
use quote::{quote, ToTokens};
use syn::spanned::Spanned;
use syn::{Ident, Signature, Type};

/// Convert a `snake_case` string to `PascalCase`
Expand Down Expand Up @@ -39,8 +41,8 @@ pub fn ident_to_field_type(ident: &Ident) -> syn::Result<FieldType> {
}
}

pub fn type_to_field_type(ty: &Type) -> syn::Result<FieldType> {
match ty {
pub fn type_to_field_type(ty: &Type) -> syn::Result<ParameterType> {
let field_type = match ty {
Type::Array(arr) => {
let elem_type = type_to_field_type(&arr.elem)?;
// convert arr.len expr to u64
Expand All @@ -53,14 +55,16 @@ pub fn type_to_field_type(ty: &Type) -> syn::Result<FieldType> {
))
}
};
Ok(FieldType::Array(len, Box::new(elem_type)))
Ok(FieldType::Array(len, Box::new(elem_type.ty)))
}
Type::Path(inner) => path_to_field_type(&inner.path),
Type::Reference(type_reference) => type_to_field_type(&type_reference.elem),
Type::Reference(type_reference) => {
type_to_field_type(&type_reference.elem).map(|ref_ty| ref_ty.ty)
}
Type::Tuple(tuple) => {
let mut ret = vec![];
for elem in &tuple.elems {
let elem_type = type_to_field_type(elem)?;
let elem_type = type_to_field_type(elem)?.ty;
ret.push(elem_type);
}
Ok(FieldType::Tuple(ret))
Expand All @@ -69,7 +73,12 @@ pub fn type_to_field_type(ty: &Type) -> syn::Result<FieldType> {
ty,
"unsupported type (type_to_field_type)",
)),
}
};

Ok(ParameterType {
ty: field_type?,
span: Some(ty.span()),
})
}

pub fn path_to_field_type(path: &syn::Path) -> syn::Result<FieldType> {
Expand All @@ -95,7 +104,7 @@ pub fn path_to_field_type(path: &syn::Path) -> syn::Result<FieldType> {
let inner_arg = &inner.args[0];
if let syn::GenericArgument::Type(inner_ty) = inner_arg {
let inner_type = type_to_field_type(inner_ty)?;
match inner_type {
match inner_type.ty {
FieldType::Uint8 => Ok(FieldType::Bytes),
others => Ok(FieldType::List(Box::new(others))),
}
Expand All @@ -111,15 +120,15 @@ pub fn path_to_field_type(path: &syn::Path) -> syn::Result<FieldType> {
if ident.eq("Option") && inner.args.len() == 1 =>
{
let inner_arg = &inner.args[0];
if let syn::GenericArgument::Type(inner_ty) = inner_arg {
let inner_type = type_to_field_type(inner_ty)?;
Ok(FieldType::Optional(Box::new(inner_type)))
} else {
Err(syn::Error::new_spanned(
let syn::GenericArgument::Type(inner_ty) = inner_arg else {
return Err(syn::Error::new_spanned(
inner_arg,
"unsupported complex type",
))
}
));
};

let inner_type = type_to_field_type(inner_ty)?;
Ok(FieldType::Optional(Box::new(inner_type.ty)))
}
// Support for Result<T, E> where T is a simple type
syn::PathArguments::AngleBracketed(inner)
Expand All @@ -128,7 +137,7 @@ pub fn path_to_field_type(path: &syn::Path) -> syn::Result<FieldType> {
let inner_arg = &inner.args[0];
if let syn::GenericArgument::Type(inner_ty) = inner_arg {
let inner_type = type_to_field_type(inner_ty)?;
Ok(inner_type)
Ok(inner_type.ty)
} else {
Err(syn::Error::new_spanned(
inner_arg,
Expand All @@ -146,7 +155,10 @@ pub fn path_to_field_type(path: &syn::Path) -> syn::Result<FieldType> {
for inner_arg in &inner.args {
if let syn::GenericArgument::Type(inner_ty) = inner_arg {
let inner_type = type_to_field_type(inner_ty)?;
ret.push((inner_ty.to_token_stream().to_string(), Box::new(inner_type)))
ret.push((
inner_ty.to_token_stream().to_string(),
Box::new(inner_type.ty),
))
} else {
return Err(syn::Error::new_spanned(inner_arg, "unsupported type param"));
}
Expand Down Expand Up @@ -174,7 +186,7 @@ pub fn get_non_job_arguments(
}

pub(crate) trait MacroExt {
fn result_to_field_types(&self, result: &Type) -> syn::Result<Vec<FieldType>> {
fn result_to_field_types(&self, result: &Type) -> syn::Result<Vec<ParameterType>> {
match self.return_type() {
ResultsKind::Infered => type_to_field_type(result).map(|x| vec![x]),
ResultsKind::Types(types) => {
Expand Down
Loading

0 comments on commit 47edd9a

Please sign in to comment.