1use crate::core;
2use anyhow::{bail, Context, Result};
3use std::collections::BTreeSet;
4use std::fmt::Debug;
5use wast::component::WastVal;
6use wast::core::NanPattern;
7
8pub use wasmtime::component::*;
9
10pub fn val(v: &WastVal<'_>) -> Result<Val> {
11 Ok(match v {
12 WastVal::Bool(b) => Val::Bool(*b),
13 WastVal::U8(b) => Val::U8(*b),
14 WastVal::S8(b) => Val::S8(*b),
15 WastVal::U16(b) => Val::U16(*b),
16 WastVal::S16(b) => Val::S16(*b),
17 WastVal::U32(b) => Val::U32(*b),
18 WastVal::S32(b) => Val::S32(*b),
19 WastVal::U64(b) => Val::U64(*b),
20 WastVal::S64(b) => Val::S64(*b),
21 WastVal::F32(b) => Val::Float32(f32::from_bits(b.bits)),
22 WastVal::F64(b) => Val::Float64(f64::from_bits(b.bits)),
23 WastVal::Char(b) => Val::Char(*b),
24 WastVal::String(s) => Val::String(s.to_string().into()),
25 WastVal::List(vals) => {
26 let vals = vals.iter().map(|v| val(v)).collect::<Result<Vec<_>>>()?;
27 Val::List(vals.into())
28 }
29 WastVal::Record(vals) => {
30 let mut fields = Vec::new();
31 for (name, v) in vals {
32 fields.push((name.to_string(), val(v)?));
33 }
34 Val::Record(fields.into())
35 }
36 WastVal::Tuple(vals) => Val::Tuple(
37 vals.iter()
38 .map(|v| val(v))
39 .collect::<Result<Vec<_>>>()?
40 .into(),
41 ),
42 WastVal::Enum(name) => Val::Enum(name.to_string()),
43 WastVal::Variant(name, payload) => {
44 let payload = payload_val(payload.as_deref())?;
45 Val::Variant(name.to_string(), payload)
46 }
47 WastVal::Option(v) => Val::Option(match v {
48 Some(v) => Some(Box::new(val(v)?)),
49 None => None,
50 }),
51 WastVal::Result(v) => Val::Result(match v {
52 Ok(v) => Ok(payload_val(v.as_deref())?),
53 Err(v) => Err(payload_val(v.as_deref())?),
54 }),
55 WastVal::Flags(v) => Val::Flags(v.iter().map(|s| s.to_string()).collect()),
56 })
57}
58
59fn payload_val(v: Option<&WastVal<'_>>) -> Result<Option<Box<Val>>> {
60 match v {
61 Some(v) => Ok(Some(Box::new(val(v)?))),
62 None => Ok(None),
63 }
64}
65
66pub fn match_val(expected: &WastVal<'_>, actual: &Val) -> Result<()> {
67 match expected {
68 WastVal::Bool(e) => match actual {
69 Val::Bool(a) => match_debug(a, e),
70 _ => mismatch(expected, actual),
71 },
72 WastVal::U8(e) => match actual {
73 Val::U8(a) => core::match_int(a, e),
74 _ => mismatch(expected, actual),
75 },
76 WastVal::S8(e) => match actual {
77 Val::S8(a) => core::match_int(a, e),
78 _ => mismatch(expected, actual),
79 },
80 WastVal::U16(e) => match actual {
81 Val::U16(a) => core::match_int(a, e),
82 _ => mismatch(expected, actual),
83 },
84 WastVal::S16(e) => match actual {
85 Val::S16(a) => core::match_int(a, e),
86 _ => mismatch(expected, actual),
87 },
88 WastVal::U32(e) => match actual {
89 Val::U32(a) => core::match_int(a, e),
90 _ => mismatch(expected, actual),
91 },
92 WastVal::S32(e) => match actual {
93 Val::S32(a) => core::match_int(a, e),
94 _ => mismatch(expected, actual),
95 },
96 WastVal::U64(e) => match actual {
97 Val::U64(a) => core::match_int(a, e),
98 _ => mismatch(expected, actual),
99 },
100 WastVal::S64(e) => match actual {
101 Val::S64(a) => core::match_int(a, e),
102 _ => mismatch(expected, actual),
103 },
104 WastVal::F32(e) => match actual {
105 Val::Float32(a) => core::match_f32(a.to_bits(), &NanPattern::Value(*e)),
106 _ => mismatch(expected, actual),
107 },
108 WastVal::F64(e) => match actual {
109 Val::Float64(a) => core::match_f64(a.to_bits(), &NanPattern::Value(*e)),
110 _ => mismatch(expected, actual),
111 },
112 WastVal::Char(e) => match actual {
113 Val::Char(a) => match_debug(a, e),
114 _ => mismatch(expected, actual),
115 },
116 WastVal::String(e) => match actual {
117 Val::String(a) => match_debug(&a[..], *e),
118 _ => mismatch(expected, actual),
119 },
120 WastVal::List(e) => match actual {
121 Val::List(a) => {
122 if e.len() != a.len() {
123 bail!("expected {} values got {}", e.len(), a.len());
124 }
125 for (i, (expected, actual)) in e.iter().zip(a.iter()).enumerate() {
126 match_val(expected, actual)
127 .with_context(|| format!("failed to match list element {i}"))?;
128 }
129 Ok(())
130 }
131 _ => mismatch(expected, actual),
132 },
133 WastVal::Record(e) => match actual {
134 Val::Record(a) => {
135 if e.len() != e.len() {
136 bail!("mismatched number of record fields");
137 }
138 for ((e_name, e_val), (a_name, a_val)) in e.iter().zip(a.iter()) {
139 if e_name != a_name {
140 bail!("expected field `{e_name}` got `{a_name}`");
141 }
142 match_val(e_val, a_val)
143 .with_context(|| format!("failed to match field `{e_name}`"))?;
144 }
145 Ok(())
146 }
147 _ => mismatch(expected, actual),
148 },
149 WastVal::Tuple(e) => match actual {
150 Val::Tuple(a) => {
151 if e.len() != a.len() {
152 bail!("expected {}-tuple, found {}-tuple", e.len(), a.len());
153 }
154 for (i, (expected, actual)) in e.iter().zip(a.iter()).enumerate() {
155 match_val(expected, actual)
156 .with_context(|| format!("failed to match tuple element {i}"))?;
157 }
158 Ok(())
159 }
160 _ => mismatch(expected, actual),
161 },
162 WastVal::Variant(name, e) => match actual {
163 Val::Variant(discr, payload) => {
164 if *discr != *name {
165 bail!("expected discriminant `{name}` got `{discr}`");
166 }
167 match_payload_val(name, e.as_deref(), payload.as_deref())
168 }
169 _ => mismatch(expected, actual),
170 },
171 WastVal::Enum(name) => match actual {
172 Val::Enum(a) => {
173 if *a != *name {
174 bail!("expected discriminant `{name}` got `{a}`");
175 } else {
176 Ok(())
177 }
178 }
179 _ => mismatch(expected, actual),
180 },
181 WastVal::Option(e) => match actual {
182 Val::Option(a) => match (e, a) {
183 (None, None) => Ok(()),
184 (Some(expected), Some(actual)) => match_val(expected, actual),
185 (None, Some(_)) => bail!("expected `none`, found `some`"),
186 (Some(_), None) => bail!("expected `some`, found `none`"),
187 },
188 _ => mismatch(expected, actual),
189 },
190 WastVal::Result(e) => match actual {
191 Val::Result(a) => match (e, a) {
192 (Ok(_), Err(_)) => bail!("expected `ok`, found `err`"),
193 (Err(_), Ok(_)) => bail!("expected `err`, found `ok`"),
194 (Err(e), Err(a)) => match_payload_val("err", e.as_deref(), a.as_deref()),
195 (Ok(e), Ok(a)) => match_payload_val("ok", e.as_deref(), a.as_deref()),
196 },
197 _ => mismatch(expected, actual),
198 },
199 WastVal::Flags(e) => match actual {
200 Val::Flags(a) => {
201 let expected = e.iter().copied().collect::<BTreeSet<_>>();
202 let actual = a.iter().map(|s| s.as_str()).collect::<BTreeSet<_>>();
203 match_debug(&actual, &expected)
204 }
205 _ => mismatch(expected, actual),
206 },
207 }
208}
209
210fn match_payload_val(
211 name: &str,
212 expected: Option<&WastVal<'_>>,
213 actual: Option<&Val>,
214) -> Result<()> {
215 match (expected, actual) {
216 (Some(e), Some(a)) => {
217 match_val(e, a).with_context(|| format!("failed to match case `{name}`"))
218 }
219 (None, None) => Ok(()),
220 (Some(_), None) => bail!("expected payload for case `{name}`"),
221 (None, Some(_)) => bail!("unexpected payload for case `{name}`"),
222 }
223}
224
225fn match_debug<T>(actual: &T, expected: &T) -> Result<()>
226where
227 T: Eq + Debug + ?Sized,
228{
229 if actual == expected {
230 Ok(())
231 } else {
232 bail!(
233 "
234 expected {expected:?}
235 actual {actual:?}"
236 )
237 }
238}
239
240fn mismatch(expected: &WastVal<'_>, actual: &Val) -> Result<()> {
241 let expected = match expected {
242 WastVal::Bool(..) => "bool",
243 WastVal::U8(..) => "u8",
244 WastVal::S8(..) => "s8",
245 WastVal::U16(..) => "u16",
246 WastVal::S16(..) => "s16",
247 WastVal::U32(..) => "u32",
248 WastVal::S32(..) => "s32",
249 WastVal::U64(..) => "u64",
250 WastVal::S64(..) => "s64",
251 WastVal::F32(..) => "f32",
252 WastVal::F64(..) => "f64",
253 WastVal::Char(..) => "char",
254 WastVal::String(..) => "string",
255 WastVal::List(..) => "list",
256 WastVal::Record(..) => "record",
257 WastVal::Tuple(..) => "tuple",
258 WastVal::Enum(..) => "enum",
259 WastVal::Variant(..) => "variant",
260 WastVal::Option(..) => "option",
261 WastVal::Result(..) => "result",
262 WastVal::Flags(..) => "flags",
263 };
264 let actual = match actual {
265 Val::Bool(..) => "bool",
266 Val::U8(..) => "u8",
267 Val::S8(..) => "s8",
268 Val::U16(..) => "u16",
269 Val::S16(..) => "s16",
270 Val::U32(..) => "u32",
271 Val::S32(..) => "s32",
272 Val::U64(..) => "u64",
273 Val::S64(..) => "s64",
274 Val::Float32(..) => "f32",
275 Val::Float64(..) => "f64",
276 Val::Char(..) => "char",
277 Val::String(..) => "string",
278 Val::List(..) => "list",
279 Val::Record(..) => "record",
280 Val::Tuple(..) => "tuple",
281 Val::Enum(..) => "enum",
282 Val::Variant(..) => "variant",
283 Val::Option(..) => "option",
284 Val::Result(..) => "result",
285 Val::Flags(..) => "flags",
286 Val::Resource(..) => "resource",
287 };
288 bail!("expected `{expected}` got `{actual}`")
289}