mod info;
use serde::Serialize;
use serde::Deserialize;
use scale_info::{IntoPortable, Registry, form::PortableForm, TypeDefPrimitive};
use scale_info::form::{Form, MetaForm};
use serde::de::DeserializeOwned;
use strum::{EnumString, Display, EnumIter, IntoEnumIterator};
use crate::info::generate_abi_type;
#[derive(Serialize, Deserialize)]
pub struct ABI {
pub contract: ContractMeta<PortableForm>,
pub methods: Vec<MethodInfo<PortableForm>>,
pub types: Vec<ABIType<PortableForm>>,
}
impl ABI {
pub fn new(meta: ContractMeta, methods: Vec<MethodInfo>) -> ABI {
let mut registry = Registry::new();
ABI {
contract: meta.into_portable(&mut registry),
methods: methods
.into_iter()
.map(|method| method.into_portable(&mut registry))
.collect::<Vec<_>>(),
types: generate_abi_type(registry),
}
}
}
#[derive(Serialize, Deserialize)]
#[serde(bound(
serialize = "F::Type: Serialize, F::String: Serialize",
deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
))]
pub struct ContractMeta<F: Form = MetaForm> {
pub name: String,
pub constructor: ConstructorSpec<F>,
}
impl IntoPortable for ContractMeta {
type Output = ContractMeta<PortableForm>;
fn into_portable(self, registry: &mut Registry) -> Self::Output {
ContractMeta {
name: self.name,
constructor: ConstructorSpec {
input: self
.constructor
.input
.into_iter()
.map(|info| info.into_portable(registry))
.collect::<Vec<_>>()
},
}
}
}
#[derive(Serialize, Deserialize)]
pub struct MutexField {
pub field_name: String,
pub field_id: i32,
pub parallel_index: Vec<i32>,
}
#[derive(Serialize, Deserialize)]
pub struct MutexCall {
pub address_index: i32,
pub address: String,
pub methods: Vec<String>,
pub cns_name: String,
pub cns_index: i32,
}
#[derive(Serialize, Deserialize)]
#[serde(bound(
serialize = "F::Type: Serialize, F::String: Serialize",
deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
))]
pub struct MethodInfo<F: Form = MetaForm> {
pub name: String,
pub input: Vec<TypeInfo<F>>,
pub output: Vec<TypeInfo<F>>,
pub parallel_level: i32,
pub mutex_fields: Vec<MutexField>,
pub mutex_calls: Vec<MutexCall>,
pub method_calls: Vec<String>,
}
impl IntoPortable for MethodInfo {
type Output = MethodInfo<PortableForm>;
fn into_portable(self, registry: &mut Registry) -> Self::Output {
MethodInfo {
name: self.name,
input: self
.input
.into_iter()
.map(|info| info.into_portable(registry))
.collect::<Vec<_>>(),
output: self
.output
.into_iter()
.map(|info| info.into_portable(registry))
.collect::<Vec<_>>(),
parallel_level: self.parallel_level,
mutex_fields: self.mutex_fields
.into_iter()
.map(|x| { x.into() })
.collect::<Vec<_>>(),
mutex_calls: self.mutex_calls
.into_iter()
.map(|x| { x.into() })
.collect::<Vec<_>>(),
method_calls: self.method_calls,
}
}
}
#[derive(Serialize, Deserialize)]
#[serde(bound(
serialize = "F::Type: Serialize, F::String: Serialize",
deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
))]
pub struct ConstructorSpec<F: Form = MetaForm> {
pub input: Vec<TypeInfo<F>>,
}
#[derive(Serialize, Deserialize)]
#[serde(bound(
serialize = "F::Type: Serialize, F::String: Serialize",
deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
))]
pub struct TypeInfo<F: Form = MetaForm> {
pub type_id: F::Type,
}
impl IntoPortable for TypeInfo {
type Output = TypeInfo<PortableForm>;
fn into_portable(self, registry: &mut Registry) -> Self::Output {
TypeInfo {
type_id: registry.register_type(&self.type_id),
}
}
}
#[derive(Serialize, Deserialize)]
#[serde(bound(
serialize = "F::Type: Serialize, F::String: Serialize",
deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned"
))]
#[serde(rename(serialize = "types"))]
pub struct ABIType<F: Form = MetaForm> {
#[serde(rename(serialize = "id"))]
pub type_id: u32,
#[serde(rename(serialize = "type"))]
pub tp: TypeEnum,
#[serde(skip_serializing_if = "Option::is_none")]
pub fields: Option<Vec<TypeInfo<F>>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub primitive: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub array_len: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub variants: Option<Vec<Vec<TypeInfo<F>>>>,
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all(serialize = "lowercase"))]
pub enum TypeEnum {
Primitive,
Struct,
Vec,
Array,
Tuple,
Enum,
}
#[derive(EnumString, Display, EnumIter, Debug, PartialEq)]
pub enum PrimitiveType {
#[strum(serialize = "bool")]
Bool,
#[strum(serialize = "char")]
Char,
#[strum(serialize = "str")]
Str,
#[strum(serialize = "u8")]
U8,
#[strum(serialize = "u16")]
U16,
#[strum(serialize = "u32")]
U32,
#[strum(serialize = "u64")]
U64,
#[strum(serialize = "u128")]
U128,
#[strum(serialize = "u256")]
U256,
#[strum(serialize = "i8")]
I8,
#[strum(serialize = "i16")]
I16,
#[strum(serialize = "i32")]
I32,
#[strum(serialize = "i64")]
I64,
#[strum(serialize = "i128")]
I128,
#[strum(serialize = "i256")]
I256,
}
impl PrimitiveType {
pub fn to_primitive_type(tp: &TypeDefPrimitive) -> Self {
match tp {
TypeDefPrimitive::Bool => PrimitiveType::Bool,
TypeDefPrimitive::Char => PrimitiveType::Char,
TypeDefPrimitive::Str => PrimitiveType::Str,
TypeDefPrimitive::U8 => PrimitiveType::U8,
TypeDefPrimitive::U16 => PrimitiveType::U16,
TypeDefPrimitive::U32 => PrimitiveType::U32,
TypeDefPrimitive::U64 => PrimitiveType::U64,
TypeDefPrimitive::U128 => PrimitiveType::U128,
TypeDefPrimitive::U256 => PrimitiveType::U256,
TypeDefPrimitive::I8 => PrimitiveType::I8,
TypeDefPrimitive::I16 => PrimitiveType::I16,
TypeDefPrimitive::I32 => PrimitiveType::I32,
TypeDefPrimitive::I64 => PrimitiveType::I64,
TypeDefPrimitive::I128 => PrimitiveType::I128,
TypeDefPrimitive::I256 => PrimitiveType::I256,
}
}
pub fn is_primitive_type(type_name: &str) -> bool {
for tp in PrimitiveType::iter() {
if tp.to_string() == type_name {
return true;
}
}
false
}
}
#[cfg(test)]
mod tests {
#![allow(dead_code)]
use crate::{ContractMeta, ConstructorSpec, MethodInfo, TypeInfo as ABITypeInfo, ABI, MutexField};
use scale_info::meta_type;
use scale_info::TypeInfo;
#[derive(TypeInfo)]
pub struct Student {
id: u32,
name: String,
list: Vec<Vec<String>>,
}
#[derive(TypeInfo)]
pub struct Info {
ages: Vec<[Student; 10]>,
}
#[test]
fn test_abi_serialized_null() {
let meta = ContractMeta {
name: "MyContract".to_string(),
constructor: ConstructorSpec {
input: vec![]
},
};
let methods = vec![];
let abi = ABI::new(meta, methods);
let json = serde_json::to_string_pretty(&abi).unwrap();
println!("{}", json);
assert_eq!(
json,
r#"{
"contract": {
"name": "MyContract",
"constructor": {
"input": []
}
},
"methods": [],
"types": []
}"#);
}
#[derive(TypeInfo)]
enum IpAddr {
V4(u32, (u32, u32)),
V6(u32),
}
#[test]
fn test_abi_serialized_normal() {
let meta = ContractMeta {
name: "MyContract".to_string(),
constructor: ConstructorSpec {
input: vec![]
},
};
let methods = vec![
MethodInfo {
name: "add".to_string(),
input: vec![
ABITypeInfo {
type_id: meta_type::<u64>(),
}
],
output: vec![
ABITypeInfo {
type_id: meta_type::<u64>(),
}
],
parallel_level: 0,
mutex_fields: vec![],
method_calls: vec![],
mutex_calls: vec![],
},
MethodInfo {
name: "make_student".to_string(),
input: vec![
ABITypeInfo {
type_id: meta_type::<Student>(),
},
ABITypeInfo {
type_id: meta_type::<Info>(),
},
],
output: vec![
ABITypeInfo {
type_id: meta_type::<u64>(),
}
],
parallel_level: 0,
mutex_fields: vec![],
method_calls: vec![],
mutex_calls: vec![],
},
MethodInfo {
name: "testTuple".to_string(),
input: vec![
ABITypeInfo {
type_id: meta_type::<(u32, u32)>()
}
],
output: vec![
ABITypeInfo {
type_id: meta_type::<IpAddr>()
},
ABITypeInfo {
type_id: meta_type::<Option<u32>>()
},
],
parallel_level: 0,
mutex_fields: vec![
MutexField { field_name: "".to_string(), parallel_index: vec![1, 2], field_id: 1 }
],
method_calls: vec![],
mutex_calls: vec![],
},
];
let abi = ABI::new(meta, methods);
let json = serde_json::to_string_pretty(&abi);
println!("{}", json.unwrap())
}
#[test]
fn test_meta_type() {
use scale_info::TypeInfo as Tp2;
let a = vec![
meta_type::<String>(),
meta_type::<&String>(),
meta_type::<&str>(),
meta_type::<&'static str>(),
meta_type::<bool>(),
meta_type::<&bool>(),
meta_type::<Box<bool>>(),
meta_type::<i128>(),
meta_type::<Student>(),
meta_type::<&Student>(),
];
for tp in a {
println!("{:?}", tp.type_info().type_def())
}
println!("{:?}", <u32 as Tp2>::type_info().type_def())
}
#[test]
fn test1() {
let a = "Hello, world";
a.to_string().trim().trim_matches('\"').split(',').for_each(|x| {
println!("{}", x.trim())
})
}
}