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