wasm_contract/
contract.rs1use std::collections::HashMap;
4
5#[derive(Debug, Clone, PartialEq, Eq, Default)]
6pub struct Contract {
7 pub imports: HashMap<(String, String), Import>,
9 pub exports: HashMap<String, Export>,
11}
12
13impl Contract {
14 pub fn merge(&self, other: Contract) -> Result<Contract, String> {
15 let mut base = self.clone();
16
17 for (key, val) in other.imports.into_iter() {
18 if base.imports.contains_key(&key) {
19 if val != base.imports[&key] {
20 return Err(format!("Conflict detected: the import \"{}\" \"{}\" was found but the definitions were different: {:?} {:?}", &key.0, &key.1, base.imports[&key], val));
21 }
22 } else {
23 let res = base.imports.insert(key, val);
24 debug_assert!(res.is_none());
25 }
26 }
27
28 for (key, val) in other.exports.into_iter() {
29 if base.exports.contains_key(&key) {
30 if val != base.exports[&key] {
31 return Err(format!("Conflict detected: the key {} was found in exports but the definitions were different: {:?} {:?}", key, base.exports[&key], val));
32 }
33 } else {
34 let res = base.exports.insert(key, val);
35 debug_assert!(res.is_none());
36 }
37 }
38 Ok(base)
39 }
40}
41
42#[derive(Debug, Clone, PartialEq, Eq)]
43pub enum Import {
44 Func {
45 namespace: String,
46 name: String,
47 params: Vec<WasmType>,
48 result: Vec<WasmType>,
49 },
50 Global {
51 namespace: String,
52 name: String,
53 var_type: WasmType,
54 },
55}
56
57impl Import {
58 pub fn format_key(ns: &str, name: &str) -> (String, String) {
59 (ns.to_string(), name.to_string())
60 }
61
62 pub fn get_key(&self) -> (String, String) {
64 match self {
65 Import::Func {
66 namespace, name, ..
67 } => Self::format_key(&namespace, &name),
68 Import::Global {
69 namespace, name, ..
70 } => Self::format_key(&namespace, &name),
71 }
72 }
73}
74
75#[derive(Debug, Clone, PartialEq, Eq)]
76pub enum Export {
77 Func {
78 name: String,
79 params: Vec<WasmType>,
80 result: Vec<WasmType>,
81 },
82 Global {
83 name: String,
84 var_type: WasmType,
85 },
86}
87
88impl Export {
89 pub fn format_key(name: &str) -> String {
90 name.to_string()
91 }
92
93 pub fn get_key(&self) -> String {
95 match self {
96 Export::Func { name, .. } => Self::format_key(&name),
97 Export::Global { name, .. } => Self::format_key(&name),
98 }
99 }
100}
101
102#[derive(Debug, Clone, PartialEq, Eq)]
104pub enum WasmType {
105 I32,
106 I64,
107 F32,
108 F64,
109}
110
111impl std::fmt::Display for WasmType {
112 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
113 write!(
114 f,
115 "{}",
116 match self {
117 WasmType::I32 => "i32",
118 WasmType::I64 => "i64",
119 WasmType::F32 => "f32",
120 WasmType::F64 => "f64",
121 }
122 )
123 }
124}
125
126#[cfg(test)]
127mod test {
128 use crate::parser;
129
130 #[test]
131 fn merging_works() {
132 let contract1_src = r#"(assert_import (func "env" "plus_one" (param i32) (result i32)))"#;
133 let contract2_src = r#"(assert_import (func "env" "plus_one" (param i64) (result i64)))"#;
134 let contract3_src = r#"(assert_import (func "env" "times_two" (param i64) (result i64)))"#;
135 let contract4_src =
136 r#"(assert_import (func "env" "times_two" (param i64 i64) (result i64)))"#;
137 let contract5_src = r#"(assert_export (func "empty_bank_account" (param) (result)))"#;
138 let contract6_src = r#"(assert_export (func "empty_bank_account" (param) (result i64)))"#;
139
140 let contract1 = parser::parse_contract(contract1_src).unwrap();
141 let contract2 = parser::parse_contract(contract2_src).unwrap();
142 let contract3 = parser::parse_contract(contract3_src).unwrap();
143 let contract4 = parser::parse_contract(contract4_src).unwrap();
144 let contract5 = parser::parse_contract(contract5_src).unwrap();
145 let contract6 = parser::parse_contract(contract6_src).unwrap();
146
147 assert!(contract1.merge(contract2.clone()).is_err());
148 assert!(contract2.merge(contract1.clone()).is_err());
149 assert!(contract1.merge(contract3.clone()).is_ok());
150 assert!(contract2.merge(contract3.clone()).is_ok());
151 assert!(contract3.merge(contract2.clone()).is_ok());
152 assert!(
153 contract1.merge(contract1.clone()).is_ok(),
154 "exact matches are accepted"
155 );
156 assert!(contract3.merge(contract4.clone()).is_err());
157 assert!(contract5.merge(contract5.clone()).is_ok());
158 assert!(contract5.merge(contract6.clone()).is_err());
159 }
160}