Unverified Commit 45c3f21c authored by Robin Freyler's avatar Robin Freyler
Browse files

[lang] Add initial implementation of JSON api generation

parent 1ef375ee
......@@ -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"
......
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);
}
......@@ -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 {
......
......@@ -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;
......@@ -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())
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment