1use serde::{
2 Deserialize,
5 Serialize,
6};
7
8use std::collections::HashMap;
9
10use eosio_scale_info::{
11 Type,
12 Path
13};
14
15pub struct ActionInfo {
16 pub name: String,
17 pub info: Type,
18}
19
20pub struct TableInfo {
21 pub name: String,
22 pub info: Type,
23}
24
25pub struct ABIInfo {
26 pub actions: Vec<ActionInfo>,
27 pub tables: Vec<TableInfo>,
28 pub structs: Vec<Type>,
29 pub variants: Vec<Type>,
30}
31
32#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
33pub struct ABIType {
34 name: String,
35 #[serde(rename = "type")]
36 ty: String,
37}
38
39#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
40pub struct ABIStruct {
41 name: String,
42 base: String,
43 fields: Vec<ABIType>,
44}
45
46#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
48pub struct ABIAction {
49 name: String,
50 #[serde(rename = "type")]
51 ty: String,
52 ricardian_contract: String,
53}
54
55#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
56pub struct ABITable {
57 name: String,
58 #[serde(rename = "type")]
59 ty: String,
60 index_type: String,
61 key_names: Vec<String>,
62 key_types: Vec<String>,
63}
64
65#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
66pub struct ABIVariant {
67 name: String,
68 types: Vec<String>,
70}
71
72#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
73pub struct ABI {
74 version: String,
75 types: Vec<String>,
76 structs: Vec<ABIStruct>,
77 actions: Vec<ABIAction>,
78 tables: Vec<ABITable>,
79 variants: Vec<ABIVariant>,
80 abi_extensions: Vec<String>,
81 error_messages: Vec<String>,
82 ricardian_clauses: Vec<String>,
83}
84
85fn native_type_to_abi_type(tp: &str) -> &str {
86 match tp {
87 "bool" => "bool",
88 "i8" => "int8",
89 "u8" => "uint8",
90 "i16" => "int16",
91 "u16" => "uint16",
92 "i32" => "int32",
93 "u32" => "uint32",
94 "i64" => "int64",
95 "u64" => "uint64",
96 "i128" => "int128",
97 "u128" => "uint128",
98 "Varint32" => "varint32",
99 "VarUint32" => "varuint32",
100 "f32" => "float32",
101 "f64" => "float64",
102 "Float128" => "float128",
103 "TimePoint" => "time_point",
104 "TimePointSec" => "time_point_sec",
105 "BlockTimeStampType" => "block_timestamp_type",
106 "Name" => "name",
107 "&[u8]" => "bytes",
108 "String" => "string",
109 "Checksum160" => "checksum160",
110 "Checksum256" => "checksum256",
111 "Uint256" => "checksum256",
112 "Checksum512" => "checksum512",
113 "PublicKey" => "public_key",
114 "Signature" => "signature",
115 "Symbol" => "symbol",
116 "SymbolCode" => "symbol_code",
117 "Asset" => "asset",
118 "ExtendedAsset" => "extended_asset",
119 _ => tp,
120 }
121}
122
123fn is_intrinsic_abi_type(name: &str) -> bool {
124 match name {
125 "bool" | "i8" | "u8" | "i16" | "u16" | "i32" | "u32" | "i64" | "u64" | "f32" | "f64" | "i128" | "u128" |
126 "String" |
127 "Varint32" | "VarUint32" | "Float128" | "TimePoint" | "TimePointSec" |
128 "BlockTimeStampType" | "Name" | "Checksum160" | "Checksum256" | "Uint256" |
129 "Checksum512" | "PublicKey" | "Signature" | "Symbol" | "SymbolCode" | "Asset" |
130 "ExtendedAsset" => {
131 return true;
132 }
133 _=> {
134 return false;
135 }
136 }
137}
138
139fn get_full_path_name(path: &Path) -> String {
140 return path.segments().to_vec().join("::");
141}
142
143fn get_last_path_name(path: &Path) -> String {
144 let len = path.segments().len();
145 if len == 0 {
146 return String::from("");
147 }
148 return String::from(path.segments()[len-1]);
149}
150
151pub fn verify_abi_structs(main_contract_structs: &Vec<Type>) -> Vec<Type> {
152 let mut main_contract_structs_map: HashMap<String, &Type> = HashMap::new();
154 let mut all_structs_map: HashMap<String, &Type> = HashMap::new();
156
157 for ty in main_contract_structs {
158 let last_name = get_last_path_name(ty.path());
159 let full_name = get_full_path_name(ty.path());
160 if let Some(ty) = main_contract_structs_map.get(&last_name) {
161 let full_name2 = get_full_path_name(ty.path());
162 if full_name != full_name2 {
163 panic!("Same struct name live in different modules is not supported by ABI\n{}\n<==>\n{}\n", full_name, full_name2);
164 }
165 } else {
166 main_contract_structs_map.insert(last_name.clone(), ty);
167 }
168
169 if let Some(ty2) = all_structs_map.get(&last_name) {
170 let full_name2 = get_full_path_name(ty2.path());
171 if full_name2 != full_name {
172 panic!("Same struct name live in different modules is not supported by ABI\n{}\n<==>\n{}\n", full_name, full_name2);
173 }
174 } else {
175 all_structs_map.insert(last_name, ty);
176 }
177 }
178
179 let hashmap_mutex = eosio_scale_info::get_scale_type_map();
180 let global_hash_map = &*hashmap_mutex.lock().unwrap();
181 for (full_name, ty) in global_hash_map {
182 let last_name = get_last_path_name(ty.path());
183 if let Some(ty) = all_structs_map.get(&last_name) {
184 let full_name2 = get_full_path_name(ty.path());
185 if full_name2 != *full_name {
186 panic!("Same struct name live in different modules is not supported by ABI\n{}\n<==>\n{}\n", *full_name, full_name2);
187 }
188 } else {
189 all_structs_map.insert(last_name, ty);
190 }
191 }
192
193 let mut other_structs_map: HashMap<String, &Type> = HashMap::new();
194
195 let mut check_rust_type = |struct_name: &str, field_name: &str, rust_type: &str| {
196 if is_intrinsic_abi_type(rust_type) {
197 return;
198 }
199
200 if let Some(_) = main_contract_structs_map.get(rust_type) {
201 return;
202 }
203
204 if let Some(ty) = all_structs_map.get(rust_type) {
205 let name = String::from(rust_type);
206 if let Some(_) = other_structs_map.get(&name) {
207 } else {
209 other_structs_map.insert(name, *ty);
210 }
211 return;
212 }
213 panic!("abi struct not found: {}.{}: {}", struct_name, field_name, rust_type);
214 };
215
216 main_contract_structs.iter().for_each(|item|{
217 let struct_name = &get_last_path_name(item.path());
218 match item.type_def() {
219 ::eosio_scale_info::TypeDef::Composite(x) => {
220 x.fields().iter().for_each(|field|{
221 let field_name = *field.name().unwrap();
222 let rust_type = *field.type_name().unwrap();
223 if let Some(_) = rust_type.find("Option<") {
224 let inner_rust_type = &rust_type["Option<".len()..rust_type.len() -1];
225 check_rust_type(struct_name, field_name, inner_rust_type);
226 } else if let Some(_) = rust_type.find("Vec<") {
227 let inner_rust_type = &rust_type["Vec<".len()..rust_type.len() -1];
228 check_rust_type(struct_name, field_name, inner_rust_type);
229 } else if let Some(_) = rust_type.find("BinaryExtension<") {
230 let inner_rust_type = &rust_type["BinaryExtension<".len()..rust_type.len() -1];
231 check_rust_type(struct_name, field_name, inner_rust_type);
232 } else {
233 check_rust_type(struct_name, field_name, rust_type);
234 }
235 });
236 }
237 ::eosio_scale_info::TypeDef::Variant(x) => {
238 x.variants().iter().for_each(|v|{
239 let name = *v.name();
240 let rust_type = v.fields()[0].type_name().unwrap();
241 check_rust_type(struct_name, name, *rust_type);
242 });
243 }
244 _ => {
245 println!("+++unknown abi type: {:?}", item);
246 }
247 }
248 });
249
250 let mut other_structs: Vec<Type> = Vec::new();
251 for (_, ty) in other_structs_map {
252 other_structs.push(ty.clone());
253 }
254 return other_structs;
255}
256
257pub fn parse_abi_info(info: &mut ABIInfo) -> String {
258 let mut abi = ABI {
259 version: String::from("eosio::abi/1.1"),
260 types: Vec::new(),
261 structs: Vec::new(),
262 actions: Vec::new(),
263 tables: Vec::new(),
264 variants: Vec::new(),
265 abi_extensions: Vec::new(),
266 error_messages: Vec::new(),
267 ricardian_clauses: Vec::new(),
268 };
269
270
271 let other_structs = verify_abi_structs(&info.structs);
272 info.structs.extend(other_structs);
273
274 info.structs.iter().for_each(|item|{
275 match item.type_def() {
276 ::eosio_scale_info::TypeDef::Composite(x) => {
277 if is_intrinsic_abi_type(&get_last_path_name(item.path())) {
278 return;
279 }
280
281 let name = item.path().segments().last().unwrap();
282 let mut s = ABIStruct{
283 name: String::from(*name),
284 base: String::from(""),
285 fields: Vec::new(),
286 };
287 x.fields().iter().for_each(|field|{
288 let ty: String;
289 let rust_type = *field.type_name().unwrap();
290 if let Some(_) = rust_type.find("Option<") {
291 ty = String::from(native_type_to_abi_type(&rust_type["Option<".len()..rust_type.len() -1])) + "?";
292 } else if let Some(_) = rust_type.find("BinaryExtension<") {
293 ty = String::from(native_type_to_abi_type(&rust_type["BinaryExtension<".len()..rust_type.len() -1])) + "$";
294 } else if let Some(_) = rust_type.find("Vec<") {
295 let inner_rust_type = &rust_type["Vec<".len()..rust_type.len() -1];
296 if inner_rust_type == "u8" {
297 ty = String::from("bytes");
298 } else {
299 ty = String::from(native_type_to_abi_type(inner_rust_type)) + "[]";
300 }
301 } else {
302 ty = String::from(native_type_to_abi_type(rust_type));
303 }
304 s.fields.push(
305 ABIType{
306 name: String::from(*field.name().unwrap()),
307 ty,
308 }
309 )
310 });
311 abi.structs.push(s);
312 }
313 ::eosio_scale_info::TypeDef::Variant(x) => {
314 let name = item.path().segments().last().unwrap();
315 let mut abi_variant = ABIVariant{
316 name: String::from(*name),
317 types: Vec::new(),
318 };
319 x.variants().iter().for_each(|v|{
320 let rust_type = v.fields()[0].type_name().unwrap();
321 abi_variant.types.push(native_type_to_abi_type(rust_type).into());
322 });
323 abi.variants.push(abi_variant);
324 }
325 _ => {
326 println!("+++unknown abi type: {:?}", item);
327 }
329 }
330 });
331
332 info.tables.iter().for_each(|table|{
333 if let ::eosio_scale_info::TypeDef::Composite(_) = table.info.type_def() {
334 let name = table.info.path().segments().last().unwrap();
335 abi.tables.push(ABITable {
336 name: table.name.clone(),
337 ty: String::from(*name),
338 index_type: String::from("i64"),
339 key_names: Vec::new(),
340 key_types: Vec::new(),
341 });
342 }
343 });
344
345 info.actions.iter().for_each(|action|{
346 if let ::eosio_scale_info::TypeDef::Composite(_) = action.info.type_def() {
347 let name = action.info.path().segments().last().unwrap();
348 abi.actions.push(ABIAction {
349 name: String::from(*name),
350 ty: String::from(*name),
351 ricardian_contract: String::from(""),
352 });
353 }
354 });
355
356 let cmp = |x: &str, y: &str| -> std::cmp::Ordering {
357 if x == y {
358 return std::cmp::Ordering::Equal;
359 }
360 if x < y {
361 return std::cmp::Ordering::Less;
362 }
363 return std::cmp::Ordering::Greater;
364 };
365
366 abi.structs.sort_by(|x, y| -> std::cmp::Ordering {
367 cmp(&x.name, &y.name)
368 });
369
370 abi.actions.sort_by(|x, y| -> std::cmp::Ordering {
371 cmp(&x.name, &y.name)
372 });
373
374 abi.tables.sort_by(|x, y| -> std::cmp::Ordering {
375 cmp(&x.name, &y.name)
376 });
377
378 abi.variants.sort_by(|x, y| -> std::cmp::Ordering {
379 cmp(&x.name, &y.name)
380 });
381
382 if let Ok(contents) = serde_json::to_string_pretty(&abi) {
383 return contents;
384 }
385 return String::from("");
386}