1use crate::component;
2use crate::prelude::*;
3use std::borrow::Cow;
4
5use super::{canonicalize_nan32, canonicalize_nan64, unwrap_2val, unwrap_val};
6use component::wasm_wave::wasm::{
7 DisplayValue, WasmFunc, WasmType, WasmTypeKind, WasmValue, WasmValueError, ensure_type_kind,
8};
9
10macro_rules! maybe_unwrap_type {
11 ($ty:expr, $case:path) => {
12 match $ty {
13 $case(v) => Some(v),
14 _ => None,
15 }
16 };
17}
18
19impl WasmType for component::Type {
20 fn kind(&self) -> WasmTypeKind {
21 match self {
22 Self::Bool => WasmTypeKind::Bool,
23 Self::S8 => WasmTypeKind::S8,
24 Self::U8 => WasmTypeKind::U8,
25 Self::S16 => WasmTypeKind::S16,
26 Self::U16 => WasmTypeKind::U16,
27 Self::S32 => WasmTypeKind::S32,
28 Self::U32 => WasmTypeKind::U32,
29 Self::S64 => WasmTypeKind::S64,
30 Self::U64 => WasmTypeKind::U64,
31 Self::Float32 => WasmTypeKind::F32,
32 Self::Float64 => WasmTypeKind::F64,
33 Self::Char => WasmTypeKind::Char,
34 Self::String => WasmTypeKind::String,
35 Self::List(_) => WasmTypeKind::List,
36 Self::Record(_) => WasmTypeKind::Record,
37 Self::Tuple(_) => WasmTypeKind::Tuple,
38 Self::Variant(_) => WasmTypeKind::Variant,
39 Self::Enum(_) => WasmTypeKind::Enum,
40 Self::Option(_) => WasmTypeKind::Option,
41 Self::Result(_) => WasmTypeKind::Result,
42 Self::Flags(_) => WasmTypeKind::Flags,
43 Self::FixedLengthList(_) => WasmTypeKind::FixedLengthList,
44
45 Self::Own(_)
46 | Self::Borrow(_)
47 | Self::Stream(_)
48 | Self::Future(_)
49 | Self::ErrorContext
50 | Self::Map(_) => WasmTypeKind::Unsupported,
51 }
52 }
53
54 fn list_element_type(&self) -> Option<Self> {
55 Some(maybe_unwrap_type!(self, Self::List)?.ty())
56 }
57
58 fn record_fields(&self) -> Box<dyn Iterator<Item = (Cow<'_, str>, Self)> + '_> {
59 let Self::Record(record) = self else {
60 return Box::new(std::iter::empty());
61 };
62 Box::new(record.fields().map(|f| (f.name.into(), f.ty.clone())))
63 }
64
65 fn tuple_element_types(&self) -> Box<dyn Iterator<Item = Self> + '_> {
66 let Self::Tuple(tuple) = self else {
67 return Box::new(std::iter::empty());
68 };
69 Box::new(tuple.types())
70 }
71
72 fn variant_cases(&self) -> Box<dyn Iterator<Item = (Cow<'_, str>, Option<Self>)> + '_> {
73 let Self::Variant(variant) = self else {
74 return Box::new(std::iter::empty());
75 };
76 Box::new(variant.cases().map(|case| (case.name.into(), case.ty)))
77 }
78
79 fn enum_cases(&self) -> Box<dyn Iterator<Item = Cow<'_, str>> + '_> {
80 let Self::Enum(enum_) = self else {
81 return Box::new(std::iter::empty());
82 };
83 Box::new(enum_.names().map(Into::into))
84 }
85
86 fn option_some_type(&self) -> Option<Self> {
87 maybe_unwrap_type!(self, Self::Option).map(|o| o.ty())
88 }
89
90 fn result_types(&self) -> Option<(Option<Self>, Option<Self>)> {
91 let result = maybe_unwrap_type!(self, Self::Result)?;
92 Some((result.ok(), result.err()))
93 }
94
95 fn flags_names(&self) -> Box<dyn Iterator<Item = Cow<'_, str>> + '_> {
96 let Self::Flags(flags) = self else {
97 return Box::new(std::iter::empty());
98 };
99 Box::new(flags.names().map(Into::into))
100 }
101}
102
103macro_rules! impl_primitives {
104 ($Self:ident, $(($case:ident, $ty:ty, $make:ident, $unwrap:ident)),*) => {
105 $(
106 fn $make(val: $ty) -> $Self {
107 $Self::$case(val)
108 }
109
110 fn $unwrap(&self) -> $ty {
111 *unwrap_val!(self, $Self::$case, stringify!($case))
112 }
113 )*
114 };
115}
116
117impl WasmValue for component::Val {
118 type Type = component::Type;
119
120 fn kind(&self) -> WasmTypeKind {
121 match self {
122 Self::Bool(_) => WasmTypeKind::Bool,
123 Self::S8(_) => WasmTypeKind::S8,
124 Self::U8(_) => WasmTypeKind::U8,
125 Self::S16(_) => WasmTypeKind::S16,
126 Self::U16(_) => WasmTypeKind::U16,
127 Self::S32(_) => WasmTypeKind::S32,
128 Self::U32(_) => WasmTypeKind::U32,
129 Self::S64(_) => WasmTypeKind::S64,
130 Self::U64(_) => WasmTypeKind::U64,
131 Self::Float32(_) => WasmTypeKind::F32,
132 Self::Float64(_) => WasmTypeKind::F64,
133 Self::Char(_) => WasmTypeKind::Char,
134 Self::String(_) => WasmTypeKind::String,
135 Self::List(_) => WasmTypeKind::List,
136 Self::Record(_) => WasmTypeKind::Record,
137 Self::Tuple(_) => WasmTypeKind::Tuple,
138 Self::Variant(..) => WasmTypeKind::Variant,
139 Self::Enum(_) => WasmTypeKind::Enum,
140 Self::Option(_) => WasmTypeKind::Option,
141 Self::Result(_) => WasmTypeKind::Result,
142 Self::Flags(_) => WasmTypeKind::Flags,
143 Self::Resource(_)
144 | Self::Stream(_)
145 | Self::Future(_)
146 | Self::ErrorContext(_)
147 | Self::Map(_) => WasmTypeKind::Unsupported,
148 Self::FixedLengthList(_) => WasmTypeKind::FixedLengthList,
149 }
150 }
151
152 impl_primitives!(
153 Self,
154 (Bool, bool, make_bool, unwrap_bool),
155 (S8, i8, make_s8, unwrap_s8),
156 (S16, i16, make_s16, unwrap_s16),
157 (S32, i32, make_s32, unwrap_s32),
158 (S64, i64, make_s64, unwrap_s64),
159 (U8, u8, make_u8, unwrap_u8),
160 (U16, u16, make_u16, unwrap_u16),
161 (U32, u32, make_u32, unwrap_u32),
162 (U64, u64, make_u64, unwrap_u64),
163 (Char, char, make_char, unwrap_char)
164 );
165
166 fn make_f32(val: f32) -> Self {
167 let val = canonicalize_nan32(val);
168 Self::Float32(val)
169 }
170 fn make_f64(val: f64) -> Self {
171 let val = canonicalize_nan64(val);
172 Self::Float64(val)
173 }
174 fn make_string(val: Cow<str>) -> Self {
175 Self::String(val.into())
176 }
177 fn make_list(
178 ty: &Self::Type,
179 vals: impl IntoIterator<Item = Self>,
180 ) -> Result<Self, WasmValueError> {
181 ensure_type_kind(ty, WasmTypeKind::List)?;
182 let val = Self::List(vals.into_iter().collect());
183 ensure_type_val(ty, &val)?;
184 Ok(val)
185 }
186 fn make_record<'a>(
187 ty: &Self::Type,
188 fields: impl IntoIterator<Item = (&'a str, Self)>,
189 ) -> Result<Self, WasmValueError> {
190 ensure_type_kind(ty, WasmTypeKind::Record)?;
191 let values: Vec<(String, Self)> = fields
192 .into_iter()
193 .map(|(name, val)| (name.to_string(), val))
194 .collect();
195 let val = Self::Record(values);
196 ensure_type_val(ty, &val)?;
197 Ok(val)
198 }
199 fn make_tuple(
200 ty: &Self::Type,
201 vals: impl IntoIterator<Item = Self>,
202 ) -> Result<Self, WasmValueError> {
203 ensure_type_kind(ty, WasmTypeKind::Tuple)?;
204 let val = Self::Tuple(vals.into_iter().collect());
205 ensure_type_val(ty, &val)?;
206 Ok(val)
207 }
208 fn make_variant(
209 ty: &Self::Type,
210 case: &str,
211 val: Option<Self>,
212 ) -> Result<Self, WasmValueError> {
213 ensure_type_kind(ty, WasmTypeKind::Variant)?;
214 let val = Self::Variant(case.to_string(), val.map(Box::new));
215 ensure_type_val(ty, &val)?;
216 Ok(val)
217 }
218 fn make_enum(ty: &Self::Type, case: &str) -> Result<Self, WasmValueError> {
219 ensure_type_kind(ty, WasmTypeKind::Enum)?;
220 let val = Self::Enum(case.to_string());
221 ensure_type_val(ty, &val)?;
222 Ok(val)
223 }
224 fn make_option(ty: &Self::Type, val: Option<Self>) -> Result<Self, WasmValueError> {
225 ensure_type_kind(ty, WasmTypeKind::Option)?;
226 let val = Self::Option(val.map(Box::new));
227 ensure_type_val(ty, &val)?;
228 Ok(val)
229 }
230 fn make_result(
231 ty: &Self::Type,
232 val: Result<Option<Self>, Option<Self>>,
233 ) -> Result<Self, WasmValueError> {
234 ensure_type_kind(ty, WasmTypeKind::Result)?;
235 let val = match val {
236 Ok(val) => Self::Result(Ok(val.map(Box::new))),
237 Err(val) => Self::Result(Err(val.map(Box::new))),
238 };
239 ensure_type_val(ty, &val)?;
240 Ok(val)
241 }
242 fn make_flags<'a>(
243 ty: &Self::Type,
244 names: impl IntoIterator<Item = &'a str>,
245 ) -> Result<Self, WasmValueError> {
246 ensure_type_kind(ty, WasmTypeKind::Flags)?;
247 let val = Self::Flags(names.into_iter().map(|n| n.to_string()).collect());
248 ensure_type_val(ty, &val)?;
249 Ok(val)
250 }
251
252 fn unwrap_f32(&self) -> f32 {
253 let val = *unwrap_val!(self, Self::Float32, "f32");
254 canonicalize_nan32(val)
255 }
256 fn unwrap_f64(&self) -> f64 {
257 let val = *unwrap_val!(self, Self::Float64, "f64");
258 canonicalize_nan64(val)
259 }
260 fn unwrap_string(&self) -> Cow<'_, str> {
261 unwrap_val!(self, Self::String, "string").into()
262 }
263 fn unwrap_list(&self) -> Box<dyn Iterator<Item = Cow<'_, Self>> + '_> {
264 let list = match self {
265 Self::List(list) | Self::FixedLengthList(list) => list,
266 _ => panic!("called unwrap_list on non-list value"),
267 };
268 Box::new(list.iter().map(cow))
269 }
270 fn unwrap_record(&self) -> Box<dyn Iterator<Item = (Cow<'_, str>, Cow<'_, Self>)> + '_> {
271 let record = unwrap_val!(self, Self::Record, "record");
272 Box::new(record.iter().map(|(name, val)| (name.into(), cow(val))))
273 }
274 fn unwrap_tuple(&self) -> Box<dyn Iterator<Item = Cow<'_, Self>> + '_> {
275 let tuple = unwrap_val!(self, Self::Tuple, "tuple");
276 Box::new(tuple.iter().map(cow))
277 }
278 fn unwrap_variant(&self) -> (Cow<'_, str>, Option<Cow<'_, Self>>) {
279 let (discriminant, payload) = unwrap_2val!(self, Self::Variant, "variant");
280 (discriminant.into(), payload.as_deref().map(cow))
281 }
282 fn unwrap_enum(&self) -> Cow<'_, str> {
283 unwrap_val!(self, Self::Enum, "enum").into()
284 }
285 fn unwrap_option(&self) -> Option<Cow<'_, Self>> {
286 unwrap_val!(self, Self::Option, "option")
287 .as_deref()
288 .map(cow)
289 }
290 fn unwrap_result(&self) -> Result<Option<Cow<'_, Self>>, Option<Cow<'_, Self>>> {
291 match unwrap_val!(self, Self::Result, "result") {
292 Ok(t) => Ok(t.as_deref().map(cow)),
293 Err(e) => Err(e.as_deref().map(cow)),
294 }
295 }
296 fn unwrap_flags(&self) -> Box<dyn Iterator<Item = Cow<'_, str>> + '_> {
297 let flags = unwrap_val!(self, Self::Flags, "flags");
298 Box::new(flags.iter().map(Into::into))
299 }
300}
301
302fn ensure_type_val(ty: &component::Type, val: &component::Val) -> Result<(), WasmValueError> {
306 let wrong_value_type = || -> Result<(), WasmValueError> {
307 Err(WasmValueError::WrongValueType {
308 ty: wasm_wave::wasm::DisplayType(ty).to_string(),
309 val: wasm_wave::wasm::DisplayValue(val).to_string(),
310 })
311 };
312
313 if ty.kind() != val.kind() {
314 return wrong_value_type();
315 }
316
317 match val {
318 component::Val::List(vals) => {
319 let list_type = ty.unwrap_list().ty();
320 for val in vals {
321 ensure_type_val(&list_type, val)?;
322 }
323 }
324 component::Val::Record(vals) => {
325 let record_handle = ty.unwrap_record();
326 for field in record_handle.fields() {
328 if !matches!(field.ty, component::Type::Option(_))
329 && !vals.iter().any(|(n, _)| n == field.name)
330 {
331 return wrong_value_type();
332 }
333 }
334 for (name, field_val) in vals.iter() {
336 if let Some(field) = record_handle.fields().find(|field| field.name == name) {
339 ensure_type_val(&field.ty, field_val)?;
340 } else {
341 return wrong_value_type();
342 }
343 }
344 }
345 component::Val::Tuple(vals) => {
346 let field_types = ty.unwrap_tuple().types();
347 if field_types.len() != vals.len() {
348 return wrong_value_type();
349 }
350 for (ty, val) in field_types.into_iter().zip(vals.iter()) {
351 ensure_type_val(&ty, val)?;
352 }
353 }
354 component::Val::Variant(name, optional_payload) => {
355 if let Some(case) = ty.unwrap_variant().cases().find(|case| case.name == name) {
356 match (optional_payload, case.ty) {
357 (None, None) => {}
358 (Some(payload), Some(payload_ty)) => ensure_type_val(&payload_ty, payload)?,
359 _ => return wrong_value_type(),
360 }
361 } else {
362 return wrong_value_type();
363 }
364 }
365 component::Val::Enum(name) => {
366 if !ty.unwrap_enum().names().any(|n| n == name) {
367 return wrong_value_type();
368 }
369 }
370 component::Val::Option(Some(some_val)) => {
371 ensure_type_val(&ty.unwrap_option().ty(), some_val.as_ref())?;
372 }
373 component::Val::Result(res_val) => {
374 let result_handle = ty.unwrap_result();
375 match res_val {
376 Ok(ok) => match (ok, result_handle.ok()) {
377 (None, None) => {}
378 (Some(ok_val), Some(ok_ty)) => ensure_type_val(&ok_ty, ok_val.as_ref())?,
379 _ => return wrong_value_type(),
380 },
381 Err(err) => match (err, result_handle.err()) {
382 (None, None) => {}
383 (Some(err_val), Some(err_ty)) => ensure_type_val(&err_ty, err_val.as_ref())?,
384 _ => return wrong_value_type(),
385 },
386 }
387 }
388 component::Val::Flags(flags) => {
389 let flags_handle = ty.unwrap_flags();
390 for flag in flags {
391 if !flags_handle.names().any(|n| n == flag) {
392 return wrong_value_type();
393 }
394 }
395 }
396 component::Val::Resource(_) => {
397 return Err(WasmValueError::UnsupportedType(
398 DisplayValue(val).to_string(),
399 ));
400 }
401
402 _ => {}
405 }
406 Ok(())
407}
408
409impl WasmFunc for component::types::ComponentFunc {
410 type Type = component::Type;
411
412 fn params(&self) -> Box<dyn Iterator<Item = Self::Type> + '_> {
413 Box::new(self.params().map(|(_n, t)| t))
414 }
415
416 fn results(&self) -> Box<dyn Iterator<Item = Self::Type> + '_> {
417 Box::new(self.results())
418 }
419}
420
421fn cow<T: Clone>(t: &T) -> Cow<'_, T> {
422 Cow::Borrowed(t)
423}
424
425#[cfg(test)]
426mod tests {
427 #[test]
428 fn component_vals_smoke_test() {
429 use crate::component::Val;
430 for (val, want) in [
431 (Val::Bool(false), "false"),
432 (Val::Bool(true), "true"),
433 (Val::S8(10), "10"),
434 (Val::S16(-10), "-10"),
435 (Val::S32(1_000_000), "1000000"),
436 (Val::S64(0), "0"),
437 (Val::U8(255), "255"),
438 (Val::U16(0), "0"),
439 (Val::U32(1_000_000), "1000000"),
440 (Val::U64(9), "9"),
441 (Val::Float32(1.5), "1.5"),
442 (Val::Float32(f32::NAN), "nan"),
443 (Val::Float32(f32::INFINITY), "inf"),
444 (Val::Float32(f32::NEG_INFINITY), "-inf"),
445 (Val::Float64(-1.5e-10), "-0.00000000015"),
446 (Val::Float64(f64::NAN), "nan"),
447 (Val::Float64(f64::INFINITY), "inf"),
448 (Val::Float64(f64::NEG_INFINITY), "-inf"),
449 (Val::Char('x'), "'x'"),
450 (Val::Char('☃'), "'☃'"),
451 (Val::Char('\''), r"'\''"),
452 (Val::Char('\0'), r"'\u{0}'"),
453 (Val::Char('\x1b'), r"'\u{1b}'"),
454 (Val::Char('😂'), r"'😂'"),
455 (Val::String("abc".into()), r#""abc""#),
456 (Val::String(r#"\☃""#.into()), r#""\\☃\"""#),
457 (Val::String("\t\r\n\0".into()), r#""\t\r\n\u{0}""#),
458 ] {
459 let got = wasm_wave::to_string(&val)
460 .unwrap_or_else(|err| panic!("failed to serialize {val:?}: {err}"));
461 assert_eq!(got, want, "for {val:?}");
462 }
463 }
464
465 #[test]
466 fn test_round_trip_floats() {
467 use crate::component::{Type, Val};
468 use std::fmt::Debug;
469
470 fn round_trip<V: wasm_wave::wasm::WasmValue + PartialEq + Debug>(ty: &V::Type, val: &V) {
471 let val_str = wasm_wave::to_string(val).unwrap();
472 let result: V = wasm_wave::from_str::<V>(ty, &val_str).unwrap();
473 assert_eq!(val, &result);
474 }
475
476 for i in 0..100 {
477 for j in 0..100 {
478 round_trip(&Type::Float32, &Val::Float32(i as f32 / j as f32));
479 round_trip(&Type::Float64, &Val::Float64(i as f64 / j as f64));
480 }
481 }
482
483 round_trip(&Type::Float32, &Val::Float32(f32::EPSILON));
484 round_trip(&Type::Float64, &Val::Float64(f64::EPSILON));
485 }
486}