rust_chain/
abi.rs

1use serde::{
2    // de::DeserializeOwned,
3    // de::Deserializer,
4    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///
47#[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    // #[serde(deserialize_with = "string_or_seq_string")]
69    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    //
153    let mut main_contract_structs_map: HashMap<String, &Type> = HashMap::new();
154    //<name, full_name>
155    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                //
208            } 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                // panic!("unknown abi type {:?}", item);
328            }
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}