1 use crate::core; 2 use anyhow::{Context, Result, bail}; 3 use json_from_wast::{ComponentConst, FloatConst}; 4 use std::collections::BTreeSet; 5 use std::fmt::Debug; 6 7 pub use wasmtime::component::*; 8 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 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 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 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 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 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 }; 294 bail!("expected `{expected}` got `{actual}`") 295 } 296