Skip to content

Commit

Permalink
[lang] Add initial implementation of JSON api generation
Browse files Browse the repository at this point in the history
  • Loading branch information
Robbepop committed Mar 19, 2019
1 parent 1ef375e commit 45c3f21
Show file tree
Hide file tree
Showing 4 changed files with 244 additions and 0 deletions.
2 changes: 2 additions & 0 deletions lang/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ proc-macro2 = "0.4"
heck = "0.3"
itertools = "0.7"
either = "1.5"
serde = { version = "1.0.89", features = ["derive"] }
serde_json = "1.0.39"

[lib]
name = "pdsl_lang"
Expand Down
235 changes: 235 additions & 0 deletions lang/src/api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
use crate::{
ast,
errors::Result,
hir,
ident_ext::IdentExt,
};
use serde::{
Deserialize,
Serialize,
};
use serde_json::{
json,
Value as JsonValue,
};

/// Describes a message parameter or return type.
#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
pub enum TypeDescription {
/// The `bool` primitive type.
Bool,
/// The `u8` primitive unsigned integer.
U8,
/// The `u16` primitive unsigned integer.
U16,
/// The `u32` primitive unsigned integer.
U32,
/// The `u64` primitive unsigned integer.
U64,
/// The `u128` primitive unsigned integer.
U128,
/// The `i8` primitive signed integer.
I8,
/// The `i16` primitive signed integer.
I16,
/// The `i32` primitive signed integer.
I32,
/// The `i64` primitive signed integer.
I64,
/// The `i128` primitive signed integer.
I128,
/// The SRML address type.
Address,
/// The SRML balance type.
Balance,
/// Custom type.
Custom(String),
}

impl From<&syn::Type> for TypeDescription {
fn from(ty: &syn::Type) -> Self {
use quote::ToTokens;
match ty.into_token_stream().to_string().as_str() {
"bool" => TypeDescription::Bool,
"u8" => TypeDescription::U8,
"u16" => TypeDescription::U16,
"u32" => TypeDescription::U32,
"u64" => TypeDescription::U64,
"u128" => TypeDescription::U128,
"i8" => TypeDescription::I8,
"i16" => TypeDescription::I16,
"i32" => TypeDescription::I32,
"i64" => TypeDescription::I64,
"i128" => TypeDescription::I128,
"Address" => TypeDescription::Address,
"Balance" => TypeDescription::Balance,
custom => TypeDescription::Custom(custom.to_owned()),
}
}
}

/// Describes a pair of parameter name and type.
#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct ParamDescription {
/// The name of the parameter.
name: String,
/// The type of the parameter.
ty: TypeDescription,
}

impl From<&syn::ArgCaptured> for ParamDescription {
fn from(arg: &syn::ArgCaptured) -> Self {
let name = match &arg.pat {
syn::Pat::Ident(ident) => ident.ident.to_owned_string(),
_ => panic!("cannot handle non-ident function arguments"),
};
Self {
name,
ty: TypeDescription::from(&arg.ty),
}
}
}

/// Describes the deploy handler of a contract.
#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct DeployDescription {
/// The parameters of the deploy handler.
params: Vec<ParamDescription>,
}

impl From<&hir::DeployHandler> for DeployDescription {
fn from(deploy_handler: &hir::DeployHandler) -> Self {
Self {
params: {
deploy_handler
.decl
.inputs
.iter()
.filter_map(|arg| {
match arg {
ast::FnArg::Captured(captured) => {
Some(ParamDescription::from(captured))
}
_ => None,
}
})
.collect::<Vec<_>>()
},
}
}
}

/// Describes the return type of a contract message.
#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct ReturnTypeDescription {
/// The optional return type.
ret_ty: Option<TypeDescription>,
}

impl ReturnTypeDescription {
/// Creates a new return type description from the given optional type.
pub fn new<T>(opt_type: T) -> Self
where
T: Into<Option<TypeDescription>>,
{
Self {
ret_ty: opt_type.into(),
}
}
}

impl From<&syn::ReturnType> for ReturnTypeDescription {
fn from(ret_ty: &syn::ReturnType) -> Self {
match ret_ty {
syn::ReturnType::Default => ReturnTypeDescription::new(None),
syn::ReturnType::Type(_, ty) => {
ReturnTypeDescription::new(Some(TypeDescription::from(&**ty)))
}
}
}
}

/// Describes a contract message.
#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct MessageDescription {
/// The name of the message.
name: String,
/// The selector hash of the message.
selector: u64,
/// If the message is allowed to mutate the contract state.
mutates: bool,
/// The parameters of the message.
params: Vec<ParamDescription>,
/// The return type of the message.
ret_ty: ReturnTypeDescription,
}

impl From<&hir::Message> for MessageDescription {
fn from(message: &hir::Message) -> Self {
Self {
name: message.sig.ident.to_owned_string(),
selector: message.selector().into(),
mutates: message.is_mut(),
params: {
message
.sig
.decl
.inputs
.iter()
.filter_map(|arg| {
match arg {
ast::FnArg::Captured(captured) => {
Some(ParamDescription::from(captured))
}
_ => None,
}
})
.collect::<Vec<_>>()
},
ret_ty: ReturnTypeDescription::from(&message.sig.decl.output),
}
}
}

/// Describes a contract.
#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct ContractDescription {
/// The name of the contract.
name: String,
/// The deploy handler of the contract.
deploy: DeployDescription,
/// The external messages of the contract.
messages: Vec<MessageDescription>,
}

impl ContractDescription {
/// Returns the name of the contract.
pub fn name(&self) -> &str {
&self.name
}
}

impl From<&hir::Contract> for ContractDescription {
fn from(contract: &hir::Contract) -> Self {
ContractDescription {
name: contract.name.to_owned_string(),
deploy: DeployDescription::from(&contract.on_deploy),
messages: {
contract
.messages
.iter()
.map(MessageDescription::from)
.collect::<Vec<_>>()
},
}
}
}

pub fn generate_api_description(contract: &hir::Contract) {
let description = ContractDescription::from(contract);
let contents = serde_json::to_string(&description).unwrap();
let mut path_buf = String::from("target/");
path_buf.push_str(description.name());
path_buf.push_str(".json");
std::fs::write(path_buf, contents);
}
5 changes: 5 additions & 0 deletions lang/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,11 @@ impl Message {
_ => panic!(),
}
}

/// Returns the message selector for this message.
pub fn selector(&self) -> u32 {
0
}
}

impl From<&ast::ItemImplMethod> for Message {
Expand Down
2 changes: 2 additions & 0 deletions lang/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ pub fn contract(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
#[macro_use]
mod errors;

mod api;
mod ast;
mod gen;
mod hir;
Expand Down Expand Up @@ -60,6 +61,7 @@ pub(crate) fn contract_gen_impl2(
) -> Result<proc_macro2::TokenStream> {
let ast_contract = parser::parse_contract(input.clone())?;
let hir_contract = hir::Contract::from_ast(&ast_contract)?;
api::generate_api_description(&hir_contract);
// gen::gir::generate(&hir_program)?;
let tokens = gen::codegen(&hir_contract);
Ok(tokens.into())
Expand Down

0 comments on commit 45c3f21

Please sign in to comment.