-
Notifications
You must be signed in to change notification settings - Fork 424
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[lang] Add initial implementation of JSON api generation
- Loading branch information
Showing
4 changed files
with
244 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters