golem_wasm/
text.rs

1// Copyright 2024-2025 Golem Cloud
2//
3// Licensed under the Golem Source License v1.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://license.golem.cloud/LICENSE
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use 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}