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