1use crate::analysis::AnalysedType;
16use crate::{IntoValueAndType, Value, ValueAndType};
17use std::borrow::Cow;
18use std::collections::HashSet;
19use wasm_wave::wasm::{WasmType, WasmTypeKind, WasmValue, WasmValueError};
20use wasm_wave::{from_str, to_string};
21
22pub fn parse_value_and_type(
23 analysed_type: &AnalysedType,
24 input: &str,
25) -> Result<ValueAndType, String> {
26 let parsed: ValueAndType = from_str(analysed_type, input).map_err(|err| err.to_string())?;
27 Ok(parsed)
28}
29
30pub fn print_value_and_type(value: &ValueAndType) -> Result<String, String> {
31 if value.typ.contains_handle() {
32 Err("Cannot print handle type".to_string())
33 } else {
34 to_string(value).map_err(|err| err.to_string())
35 }
36}
37
38impl WasmValue for ValueAndType {
39 type Type = AnalysedType;
40
41 fn kind(&self) -> WasmTypeKind {
42 self.typ.kind()
43 }
44
45 fn make_bool(val: bool) -> Self {
46 val.into_value_and_type()
47 }
48
49 fn make_s8(val: i8) -> Self {
50 val.into_value_and_type()
51 }
52
53 fn make_s16(val: i16) -> Self {
54 val.into_value_and_type()
55 }
56
57 fn make_s32(val: i32) -> Self {
58 val.into_value_and_type()
59 }
60
61 fn make_s64(val: i64) -> Self {
62 val.into_value_and_type()
63 }
64
65 fn make_u8(val: u8) -> Self {
66 val.into_value_and_type()
67 }
68
69 fn make_u16(val: u16) -> Self {
70 val.into_value_and_type()
71 }
72
73 fn make_u32(val: u32) -> Self {
74 val.into_value_and_type()
75 }
76
77 fn make_u64(val: u64) -> Self {
78 val.into_value_and_type()
79 }
80
81 fn make_f32(val: f32) -> Self {
82 val.into_value_and_type()
83 }
84
85 fn make_f64(val: f64) -> Self {
86 val.into_value_and_type()
87 }
88
89 fn make_char(val: char) -> Self {
90 val.into_value_and_type()
91 }
92
93 fn make_string(val: Cow<str>) -> Self {
94 val.to_string().into_value_and_type()
95 }
96
97 fn make_list(
98 ty: &Self::Type,
99 vals: impl IntoIterator<Item = Self>,
100 ) -> Result<Self, WasmValueError> {
101 Ok(ValueAndType {
102 value: Value::List(vals.into_iter().map(|vnt| vnt.value).collect()),
103 typ: ty.clone(),
104 })
105 }
106
107 fn make_record<'a>(
108 ty: &Self::Type,
109 fields: impl IntoIterator<Item = (&'a str, Self)>,
110 ) -> Result<Self, WasmValueError> {
111 Ok(ValueAndType {
112 value: Value::Record(fields.into_iter().map(|(_, vnt)| vnt.value).collect()),
113 typ: ty.clone(),
114 })
115 }
116
117 fn make_tuple(
118 ty: &Self::Type,
119 vals: impl IntoIterator<Item = Self>,
120 ) -> Result<Self, WasmValueError> {
121 Ok(ValueAndType {
122 value: Value::Tuple(vals.into_iter().map(|vnt| vnt.value).collect()),
123 typ: ty.clone(),
124 })
125 }
126
127 fn make_variant(
128 ty: &Self::Type,
129 case: &str,
130 val: Option<Self>,
131 ) -> Result<Self, WasmValueError> {
132 if let AnalysedType::Variant(typ) = ty {
133 let case_idx = typ
134 .cases
135 .iter()
136 .position(|pair| pair.name == case)
137 .ok_or_else(|| WasmValueError::UnknownCase(case.to_string()))?
138 as u32;
139 Ok(ValueAndType {
140 value: Value::Variant {
141 case_idx,
142 case_value: val.map(|vnt| Box::new(vnt.value)),
143 },
144 typ: ty.clone(),
145 })
146 } else {
147 Err(WasmValueError::WrongTypeKind {
148 kind: WasmTypeKind::Variant,
149 ty: ty.kind().to_string(),
150 })
151 }
152 }
153
154 fn make_enum(ty: &Self::Type, case: &str) -> Result<Self, WasmValueError> {
155 if let AnalysedType::Enum(typ) = ty {
156 let case_idx = typ
157 .cases
158 .iter()
159 .position(|c| c == case)
160 .ok_or_else(|| WasmValueError::UnknownCase(case.to_string()))?
161 as u32;
162 Ok(ValueAndType {
163 value: Value::Enum(case_idx),
164 typ: ty.clone(),
165 })
166 } else {
167 Err(WasmValueError::WrongTypeKind {
168 kind: WasmTypeKind::Enum,
169 ty: ty.kind().to_string(),
170 })
171 }
172 }
173
174 fn make_option(ty: &Self::Type, val: Option<Self>) -> Result<Self, WasmValueError> {
175 Ok(ValueAndType {
176 value: Value::Option(val.map(|vnt| Box::new(vnt.value))),
177 typ: ty.clone(),
178 })
179 }
180
181 fn make_result(
182 ty: &Self::Type,
183 val: Result<Option<Self>, Option<Self>>,
184 ) -> Result<Self, WasmValueError> {
185 Ok(ValueAndType {
186 value: Value::Result(
187 val.map(|maybe_ok| maybe_ok.map(|vnt| Box::new(vnt.value)))
188 .map_err(|maybe_err| maybe_err.map(|vnt| Box::new(vnt.value))),
189 ),
190 typ: ty.clone(),
191 })
192 }
193
194 fn make_flags<'a>(
195 ty: &Self::Type,
196 names: impl IntoIterator<Item = &'a str>,
197 ) -> Result<Self, WasmValueError> {
198 if let AnalysedType::Flags(typ) = ty {
199 let mut bitmap = Vec::new();
200 let names: HashSet<&'a str> = HashSet::from_iter(names);
201 for name in &typ.names {
202 bitmap.push(names.contains(name.as_str()));
203 }
204 Ok(ValueAndType {
205 value: Value::Flags(bitmap),
206 typ: ty.clone(),
207 })
208 } else {
209 Err(WasmValueError::WrongTypeKind {
210 kind: WasmTypeKind::Flags,
211 ty: ty.kind().to_string(),
212 })
213 }
214 }
215
216 fn unwrap_bool(&self) -> bool {
217 match self.value {
218 Value::Bool(val) => val,
219 _ => panic!("Expected bool, found {self:?}"),
220 }
221 }
222
223 fn unwrap_s8(&self) -> i8 {
224 match self.value {
225 Value::S8(val) => val,
226 _ => panic!("Expected s8, found {self:?}"),
227 }
228 }
229
230 fn unwrap_s16(&self) -> i16 {
231 match self.value {
232 Value::S16(val) => val,
233 _ => panic!("Expected s16, found {self:?}"),
234 }
235 }
236
237 fn unwrap_s32(&self) -> i32 {
238 match self.value {
239 Value::S32(val) => val,
240 _ => panic!("Expected s32, found {self:?}"),
241 }
242 }
243
244 fn unwrap_s64(&self) -> i64 {
245 match self.value {
246 Value::S64(val) => val,
247 _ => panic!("Expected s64, found {self:?}"),
248 }
249 }
250
251 fn unwrap_u8(&self) -> u8 {
252 match self.value {
253 Value::U8(val) => val,
254 _ => panic!("Expected u8, found {self:?}"),
255 }
256 }
257
258 fn unwrap_u16(&self) -> u16 {
259 match self.value {
260 Value::U16(val) => val,
261 _ => panic!("Expected u16, found {self:?}"),
262 }
263 }
264
265 fn unwrap_u32(&self) -> u32 {
266 match self.value {
267 Value::U32(val) => val,
268 _ => panic!("Expected u32, found {self:?}"),
269 }
270 }
271
272 fn unwrap_u64(&self) -> u64 {
273 match self.value {
274 Value::U64(val) => val,
275 _ => panic!("Expected u64, found {self:?}"),
276 }
277 }
278
279 fn unwrap_f32(&self) -> f32 {
280 match self.value {
281 Value::F32(val) => val,
282 _ => panic!("Expected f32, found {self:?}"),
283 }
284 }
285
286 fn unwrap_f64(&self) -> f64 {
287 match self.value {
288 Value::F64(val) => val,
289 _ => panic!("Expected f64, found {self:?}"),
290 }
291 }
292
293 fn unwrap_char(&self) -> char {
294 match self.value {
295 Value::Char(val) => val,
296 _ => panic!("Expected char, found {self:?}"),
297 }
298 }
299
300 fn unwrap_string(&self) -> Cow<'_, str> {
301 match &self.value {
302 Value::String(val) => Cow::Borrowed(val),
303 _ => panic!("Expected string, found {self:?}"),
304 }
305 }
306
307 fn unwrap_list(&self) -> Box<dyn Iterator<Item = Cow<'_, Self>> + '_> {
308 match (&self.value, &self.typ) {
309 (Value::List(vals), AnalysedType::List(typ)) => Box::new(vals.iter().map(|v| {
310 Cow::Owned(ValueAndType {
311 value: v.clone(),
312 typ: (*typ.inner).clone(),
313 })
314 })),
315 _ => panic!("Expected list, found {self:?}"),
316 }
317 }
318
319 fn unwrap_record(&self) -> Box<dyn Iterator<Item = (Cow<'_, str>, Cow<'_, Self>)> + '_> {
320 match (&self.value, &self.typ) {
321 (Value::Record(vals), AnalysedType::Record(typ)) => {
322 Box::new(vals.iter().zip(typ.fields.iter()).map(|(v, f)| {
323 (
324 Cow::Borrowed(f.name.as_str()),
325 Cow::Owned(ValueAndType {
326 value: v.clone(),
327 typ: f.typ.clone(),
328 }),
329 )
330 }))
331 }
332 _ => panic!("Expected record, found {self:?}"),
333 }
334 }
335
336 fn unwrap_tuple(&self) -> Box<dyn Iterator<Item = Cow<'_, Self>> + '_> {
337 match (&self.value, &self.typ) {
338 (Value::Tuple(vals), AnalysedType::Tuple(typ)) => {
339 Box::new(vals.iter().zip(typ.items.iter()).map(|(v, t)| {
340 Cow::Owned(ValueAndType {
341 value: v.clone(),
342 typ: t.clone(),
343 })
344 }))
345 }
346 _ => panic!("Expected tuple, found {self:?}"),
347 }
348 }
349
350 fn unwrap_variant(&self) -> (Cow<'_, str>, Option<Cow<'_, Self>>) {
351 match (&self.value, &self.typ) {
352 (
353 Value::Variant {
354 case_idx,
355 case_value,
356 },
357 AnalysedType::Variant(typ),
358 ) => {
359 let case_name = &typ.cases[*case_idx as usize].name;
360 let case_value = case_value.as_ref().map(|v| {
361 let typ = &typ.cases[*case_idx as usize].typ;
362 Cow::Owned(ValueAndType {
363 value: *v.clone(),
364 typ: typ
365 .as_ref()
366 .unwrap_or_else(|| {
367 panic!("No type information for non-unit variant case {case_name}")
368 })
369 .clone(),
370 })
371 });
372 (Cow::Borrowed(case_name), case_value)
373 }
374 _ => panic!("Expected variant, found {self:?}"),
375 }
376 }
377
378 fn unwrap_enum(&self) -> Cow<'_, str> {
379 match (&self.value, &self.typ) {
380 (Value::Enum(case_idx), AnalysedType::Enum(typ)) => {
381 Cow::Borrowed(&typ.cases[*case_idx as usize])
382 }
383 _ => panic!("Expected enum, found {self:?}"),
384 }
385 }
386
387 fn unwrap_option(&self) -> Option<Cow<'_, Self>> {
388 match (&self.value, &self.typ) {
389 (Value::Option(Some(val)), AnalysedType::Option(typ)) => {
390 Some(Cow::Owned(ValueAndType {
391 value: *val.clone(),
392 typ: (*typ.inner).clone(),
393 }))
394 }
395 (Value::Option(None), AnalysedType::Option(_)) => None,
396 _ => panic!("Expected option, found {self:?}"),
397 }
398 }
399
400 fn unwrap_result(&self) -> Result<Option<Cow<'_, Self>>, Option<Cow<'_, Self>>> {
401 match (&self.value, &self.typ) {
402 (Value::Result(Ok(Some(val))), AnalysedType::Result(typ)) => {
403 Ok(Some(Cow::Owned(ValueAndType {
404 value: *val.clone(),
405 typ: *typ
406 .ok
407 .as_ref()
408 .expect("No type information for non-unit ok value")
409 .clone(),
410 })))
411 }
412 (Value::Result(Ok(None)), AnalysedType::Result(_)) => Ok(None),
413 (Value::Result(Err(Some(val))), AnalysedType::Result(typ)) => {
414 Err(Some(Cow::Owned(ValueAndType {
415 value: *val.clone(),
416 typ: *typ
417 .err
418 .as_ref()
419 .expect("No type information for non-unit error value")
420 .clone(),
421 })))
422 }
423 (Value::Result(Err(None)), AnalysedType::Result(_)) => Err(None),
424 _ => panic!("Expected result, found {self:?}"),
425 }
426 }
427
428 fn unwrap_flags(&self) -> Box<dyn Iterator<Item = Cow<'_, str>> + '_> {
429 match (&self.value, &self.typ) {
430 (Value::Flags(bitmap), AnalysedType::Flags(typ)) => Box::new(
431 bitmap
432 .iter()
433 .zip(typ.names.iter())
434 .filter_map(|(is_set, name)| {
435 if *is_set {
436 Some(Cow::Borrowed(name.as_str()))
437 } else {
438 None
439 }
440 }),
441 ),
442 _ => panic!("Expected flags, found {self:?}"),
443 }
444 }
445}
446
447#[cfg(test)]
448mod tests {
449 use test_r::test;
450
451 use crate::analysis::analysed_type::{
452 bool, case, chr, f32, f64, field, flags, list, option, r#enum, record, result_err,
453 result_ok, s16, s32, s64, s8, str, tuple, u16, u32, u64, u8, unit_case, variant,
454 };
455 use crate::analysis::AnalysedType;
456 use crate::text::{parse_value_and_type, print_value_and_type};
457 use crate::{Value, ValueAndType};
458
459 fn round_trip(value: Value, typ: AnalysedType) {
460 let typed_value = ValueAndType::new(value.clone(), typ.clone());
461
462 let s = print_value_and_type(&typed_value).unwrap();
463 let round_trip_value: ValueAndType = parse_value_and_type(&typ, &s).unwrap();
464 let result: Value = Value::from(round_trip_value);
465 assert_eq!(value, result);
466 }
467
468 #[test]
469 fn round_trip_u8() {
470 round_trip(Value::U8(42), u8());
471 }
472
473 #[test]
474 fn round_trip_u16() {
475 round_trip(Value::U16(1234), u16());
476 }
477
478 #[test]
479 fn round_trip_u32() {
480 round_trip(Value::U32(123456), u32());
481 }
482
483 #[test]
484 fn round_trip_u64() {
485 round_trip(Value::U64(1234567890123456), u64());
486 }
487
488 #[test]
489 fn round_trip_s8() {
490 round_trip(Value::S8(-42), s8());
491 }
492
493 #[test]
494 fn round_trip_s16() {
495 round_trip(Value::S16(-1234), s16());
496 }
497
498 #[test]
499 fn round_trip_s32() {
500 round_trip(Value::S32(-123456), s32());
501 }
502
503 #[test]
504 fn round_trip_s64() {
505 round_trip(Value::S64(-1234567890123456), s64());
506 }
507
508 #[test]
509 fn round_trip_f32() {
510 round_trip(Value::F32(1234.5678), f32());
511 }
512
513 #[test]
514 fn round_trip_f64() {
515 round_trip(Value::F64(1_234_567_890_123_456.8), f64());
516 }
517
518 #[test]
519 fn round_trip_bool() {
520 round_trip(Value::Bool(true), bool());
521 }
522
523 #[test]
524 fn round_trip_char() {
525 round_trip(Value::Char('a'), chr());
526 }
527
528 #[test]
529 fn round_trip_string() {
530 round_trip(Value::String("hello".to_string()), str());
531 }
532
533 #[test]
534 fn round_trip_list_1() {
535 round_trip(
536 Value::List(vec![Value::U8(1), Value::U8(2), Value::U8(3)]),
537 list(u8()),
538 );
539 }
540
541 #[test]
542 fn round_trip_list_2() {
543 round_trip(
544 Value::List(vec![Value::List(vec![
545 Value::String("hello".to_string()),
546 Value::String("world".to_string()),
547 ])]),
548 list(list(str())),
549 );
550 }
551
552 #[test]
553 fn round_trip_record() {
554 round_trip(
555 Value::Record(vec![
556 Value::U8(1),
557 Value::String("hello".to_string()),
558 Value::Bool(true),
559 ]),
560 record(vec![
561 field("a", u8()),
562 field("b", str()),
563 field("c", bool()),
564 ]),
565 );
566 }
567
568 #[test]
569 fn round_trip_tuple() {
570 round_trip(
571 Value::Tuple(vec![
572 Value::U8(1),
573 Value::String("hello".to_string()),
574 Value::Bool(true),
575 ]),
576 tuple(vec![u8(), str(), bool()]),
577 );
578 }
579
580 #[test]
581 fn round_trip_variant() {
582 round_trip(
583 Value::Variant {
584 case_idx: 1,
585 case_value: Some(Box::new(Value::String("hello".to_string()))),
586 },
587 variant(vec![unit_case("A"), case("B", str())]),
588 );
589 }
590
591 #[test]
592 fn round_trip_enum() {
593 round_trip(Value::Enum(1), r#enum(&["A", "B"]));
594 }
595
596 #[test]
597 fn round_trip_option() {
598 round_trip(Value::Option(Some(Box::new(Value::U8(1)))), option(u8()));
599 }
600
601 #[test]
602 fn round_trip_result_ok() {
603 round_trip(
604 Value::Result(Ok(Some(Box::new(Value::U8(1))))),
605 result_ok(u8()),
606 );
607 }
608
609 #[test]
610 fn round_trip_result_err() {
611 round_trip(
612 Value::Result(Err(Some(Box::new(Value::U8(1))))),
613 result_err(u8()),
614 );
615 }
616
617 #[test]
618 fn round_trip_flags() {
619 round_trip(
620 Value::Flags(vec![true, false, true]),
621 flags(&["A", "B", "C"]),
622 );
623 }
624}