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

App Schema #200

Merged
merged 12 commits into from Dec 28, 2020
2 changes: 2 additions & 0 deletions crates/svm-sdk-tests/src/lib.rs
@@ -1,5 +1,7 @@
#![allow(unused)]

extern crate svm_sdk;

use svm_sdk::host::MockHost;
use svm_sdk::storage::MockStorage;
use svm_sdk::traits::Encoder;
Expand Down
2 changes: 2 additions & 0 deletions crates/svm-sdk/macros/Cargo.toml
Expand Up @@ -26,3 +26,5 @@ trybuild = { version = "1.0", features = ["diff"] }
default = ["mock"]
ffi = ["svm-sdk-host/ffi", "svm-sdk-storage/ffi"]
mock = ["svm-sdk-host/mock", "svm-sdk-storage/mock"]
api = []
layout = []
8 changes: 5 additions & 3 deletions crates/svm-sdk/macros/src/app.rs
Expand Up @@ -6,7 +6,7 @@ use syn::{
ItemType, ItemUse, Result,
};

use crate::{Struct, Function};
use crate::{schema, Struct, Function, Schema};
use super::{r#struct, function};

use r#struct::has_storage_attr;
Expand Down Expand Up @@ -41,7 +41,7 @@ impl App {
}
}

pub fn expand(_args: TokenStream, input: TokenStream) -> Result<TokenStream> {
pub fn expand(_args: TokenStream, input: TokenStream) -> Result<(Schema, TokenStream)> {
let module = syn::parse2(input)?;
let app = parse_app(module)?;

Expand All @@ -52,6 +52,8 @@ pub fn expand(_args: TokenStream, input: TokenStream) -> Result<TokenStream> {
let functions = expand_functions(&app)?;
let alloc_func = alloc_func_ast();

let schema = schema::app_schema(&app);

let ast = quote! {
// #(#imports)*

Expand All @@ -64,7 +66,7 @@ pub fn expand(_args: TokenStream, input: TokenStream) -> Result<TokenStream> {
#functions
};

Ok(ast)
Ok((schema,ast))
}

pub fn parse_app(mut raw_app: ItemMod) -> Result<App> {
Expand Down
8 changes: 6 additions & 2 deletions crates/svm-sdk/macros/src/function/mod.rs
Expand Up @@ -9,7 +9,11 @@ mod endpoint;
mod fundable;
mod fundable_hook;

use attr::{has_ctor_attr, has_endpoint_attr, has_fundable_hook_attr, FuncAttr, FuncAttrKind};
use attr::{FuncAttr, FuncAttrKind};

pub use attr::{
func_attrs, has_ctor_attr, has_endpoint_attr, has_fundable_attr, has_fundable_hook_attr,
};

pub struct Function {
raw_func: ItemFn,
Expand Down Expand Up @@ -38,7 +42,7 @@ impl Function {
}

pub fn expand(func: &Function) -> Result<TokenStream> {
let attrs = attr::func_attrs(func)?;
let attrs = func_attrs(func)?;

validate_attrs(&attrs)?;

Expand Down
9 changes: 7 additions & 2 deletions crates/svm-sdk/macros/src/lib.rs
Expand Up @@ -2,10 +2,15 @@

mod app;
mod function;
mod schema;
mod r#struct;

use app::App;
use function::Function;
use r#struct::Struct;
use r#struct::storage_vars;
use r#struct::{Struct, Var};

use schema::{Export, Schema, Signature};

#[proc_macro_attribute]
pub fn app(
Expand All @@ -14,6 +19,6 @@ pub fn app(
) -> proc_macro::TokenStream {
match app::expand(args.into(), input.into()) {
Err(err) => err.to_compile_error().into(),
Ok(output) => output.into(),
Ok((schema, ast)) => ast.into(),
}
}
167 changes: 167 additions & 0 deletions crates/svm-sdk/macros/src/schema.rs
@@ -0,0 +1,167 @@
use quote::quote;
use syn::{FnArg, PatType};

use crate::function::{func_attrs, has_ctor_attr, has_endpoint_attr, has_fundable_attr};
use crate::r#struct::has_storage_attr;
use crate::storage_vars;
use crate::{App, Function, Var};

#[derive(Debug)]
pub struct Schema {
exports: Vec<Export>,

storage: Vec<Var>,
}

#[derive(Debug)]
pub struct Export {
is_ctor: bool,

is_fundable: bool,

api_name: String,

wasm_name: String,

signature: Signature,
}

#[derive(Debug)]
pub struct Signature {
params: Vec<(String, String)>,

returns: Vec<String>,
}

impl Signature {
pub fn new() -> Self {
Self {
params: Vec::new(),
returns: Vec::new(),
}
}

pub fn add_param(&mut self, name: String, ty: String) {
self.params.push((name, ty));
}

pub fn add_return(&mut self, ty: String) {
self.returns.push(ty);
}

pub fn params(&self) -> &[(String, String)] {
&self.params
}

pub fn returns(&self) -> &[String] {
&self.returns
}
}

impl Schema {
pub fn new() -> Self {
Self {
exports: Vec::new(),
storage: Vec::new(),
}
}

pub fn add_export(&mut self, export: Export) {
self.exports.push(export);
}

pub fn endpoints(&self) -> Vec<&Export> {
self.exports
.iter()
.filter(|exp| exp.is_ctor == false)
.collect()
}

pub fn ctors(&self) -> Vec<&Export> {
self.exports.iter().filter(|exp| exp.is_ctor).collect()
}

pub fn exports(&self) -> &[Export] {
&self.exports
}

pub fn storage(&self) -> &[Var] {
&self.storage
}
}

pub fn app_schema(app: &App) -> Schema {
let storage = storage_schema(app);

let exports = app
.functions()
.iter()
.filter(|func| {
let attrs = func_attrs(func).unwrap();

let is_endpoint = has_endpoint_attr(&attrs);
let is_ctor = has_ctor_attr(&attrs);

is_endpoint || is_ctor
})
.map(export_schema)
.collect();

Schema { storage, exports }
}

fn storage_schema(app: &App) -> Vec<Var> {
let storage = app.structs().iter().find(|s| {
let attrs = s.attrs().as_ref().unwrap();

has_storage_attr(attrs)
});

if let Some(storage) = storage {
storage_vars(&storage).unwrap()
} else {
Vec::new()
}
}

fn export_schema(func: &Function) -> Export {
let attrs = func_attrs(func).unwrap();

let is_ctor = has_ctor_attr(&attrs);
let is_fundable = has_fundable_attr(&attrs);

let api_name = func.raw_name().to_string();

// TODO: future PR will uglify the name of the endpoint
// in order to save space in the transactions.
// The original (code) name will appear in the `schema.json` (off-chain).
let wasm_name = func.raw_name().to_string();
let signature = function_sig(func);

Export {
is_ctor,
is_fundable,
api_name,
wasm_name,
signature,
}
}

fn function_sig(func: &Function) -> Signature {
let raw_sig = func.raw_sig();

let mut sig = Signature::new();

for input in &raw_sig.inputs {
if let FnArg::Typed(PatType { pat, ty, .. }) = input {
let name = quote! { #pat };
let ty = quote! { #ty };

sig.add_param(name.to_string(), ty.to_string());
} else {
unreachable!()
}
}

sig
}
1 change: 1 addition & 0 deletions crates/svm-sdk/macros/src/struct/mod.rs
Expand Up @@ -10,6 +10,7 @@ mod storage;
mod var;

pub use attr::{has_storage_attr, StructAttr, StructAttrKind};
pub use storage::storage_vars;
pub use var::{Var, VarId};

pub struct Struct {
Expand Down
2 changes: 1 addition & 1 deletion crates/svm-sdk/macros/src/struct/storage.rs
Expand Up @@ -33,7 +33,7 @@ pub fn expand(strukt: &Struct, attrs: &[StructAttr]) -> Result<TokenStream> {
Ok(ast)
}

fn storage_vars(strukt: &Struct) -> Result<Vec<Var>> {
pub fn storage_vars(strukt: &Struct) -> Result<Vec<Var>> {
let mut vars = Vec::new();
let mut id = VarId(0);

Expand Down
19 changes: 19 additions & 0 deletions crates/svm-sdk/macros/src/struct/var.rs
@@ -1,3 +1,5 @@
use std::fmt;

use proc_macro2::{Ident, TokenStream};

use quote::{quote, ToTokens};
Expand All @@ -19,6 +21,23 @@ pub enum Var {
},
}

impl fmt::Debug for Var {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Var::Primitive {
id, name, ty_str, ..
} => writeln!(f, "Var #{} - {}: {}", id.0, name, ty_str),
Var::Array {
id,
name,
ty_str,
length,
..
} => writeln!(f, "Var #{} - {}: [{}; {}]", id.0, name, ty_str, length),
}
}
}

#[repr(transparent)]
#[derive(Debug, PartialEq, Hash, Copy, Clone)]
pub struct VarId(pub u32);
Expand Down