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