Skip to main content

wasm_wave/value/
convert.rs

1use alloc::{
2    string::{String, ToString},
3    vec::Vec,
4};
5
6use crate::{
7    WasmValue,
8    value::ValueEnum,
9    wasm::{WasmType, WasmTypeKind},
10};
11
12use super::{Type, Value};
13
14pub fn from_wasm_type(ty: &impl WasmType) -> Option<Type> {
15    if let Some(ty) = Type::simple(ty.kind()) {
16        return Some(ty);
17    }
18    Some(match ty.kind() {
19        WasmTypeKind::List => Type::list(from_wasm_type(&ty.list_element_type()?)?),
20        WasmTypeKind::Record => Type::record(
21            ty.record_fields()
22                .map(|(name, ty)| Some((name, from_wasm_type(&ty)?)))
23                .collect::<Option<Vec<_>>>()?,
24        )?,
25        WasmTypeKind::Tuple => Type::tuple(
26            ty.tuple_element_types()
27                .map(|ty| from_wasm_type(&ty))
28                .collect::<Option<Vec<_>>>()?,
29        )?,
30        WasmTypeKind::Variant => Type::variant(
31            ty.variant_cases()
32                .map(|(name, payload)| Some((name, from_optional_wasm_type(payload)?)))
33                .collect::<Option<Vec<_>>>()?,
34        )?,
35        WasmTypeKind::Enum => Type::enum_ty(ty.enum_cases())?,
36        WasmTypeKind::Option => Type::option(from_wasm_type(&ty.option_some_type()?)?),
37        WasmTypeKind::Result => {
38            let (ok, err) = ty.result_types()?;
39            Type::result(from_optional_wasm_type(ok)?, from_optional_wasm_type(err)?)
40        }
41        WasmTypeKind::Flags => Type::flags(ty.flags_names())?,
42        WasmTypeKind::Map => {
43            let (key, value) = ty.map_key_value_types()?;
44            Type::map(from_wasm_type(&key)?, from_wasm_type(&value)?)?
45        }
46        _ => return None,
47    })
48}
49
50fn from_optional_wasm_type(ty: Option<impl WasmType>) -> Option<Option<Type>> {
51    Some(match ty {
52        Some(ty) => Some(from_wasm_type(&ty)?),
53        None => None,
54    })
55}
56
57trait ValueTyped {
58    fn value_type() -> Type;
59}
60
61macro_rules! impl_primitives {
62    ($Self:ty, $(($case:ident, $ty:ty)),*) => {
63        $(
64            impl ValueTyped for $ty {
65                fn value_type() -> Type {
66                    Type::must_simple(WasmTypeKind::$case)
67                }
68            }
69
70            impl From<$ty> for $Self {
71                fn from(value: $ty) -> Self {
72                    Self(ValueEnum::$case(value))
73                }
74            }
75        )*
76    };
77}
78
79impl_primitives!(
80    Value,
81    (Bool, bool),
82    (S8, i8),
83    (S16, i16),
84    (S32, i32),
85    (S64, i64),
86    (U8, u8),
87    (U16, u16),
88    (U32, u32),
89    (U64, u64),
90    (F32, f32),
91    (F64, f64),
92    (Char, char)
93);
94
95impl ValueTyped for String {
96    fn value_type() -> Type {
97        Type::STRING
98    }
99}
100
101impl From<String> for Value {
102    fn from(value: String) -> Self {
103        Self(ValueEnum::String(value.into()))
104    }
105}
106
107impl ValueTyped for &str {
108    fn value_type() -> Type {
109        String::value_type()
110    }
111}
112
113impl<'a> From<&'a str> for Value {
114    fn from(value: &'a str) -> Self {
115        value.to_string().into()
116    }
117}
118
119impl<const N: usize, T: ValueTyped> ValueTyped for [T; N] {
120    fn value_type() -> Type {
121        Type::list(T::value_type())
122    }
123}
124
125impl<const N: usize, T: ValueTyped + Into<Value>> From<[T; N]> for Value {
126    fn from(values: [T; N]) -> Self {
127        let ty = Vec::<T>::value_type();
128        let values = values.into_iter().map(Into::into);
129        Value::make_list(&ty, values).unwrap()
130    }
131}
132
133impl<T: ValueTyped> ValueTyped for Vec<T> {
134    fn value_type() -> Type {
135        Type::list(T::value_type())
136    }
137}
138
139impl<T: ValueTyped + Into<Value>> From<Vec<T>> for Value {
140    fn from(values: Vec<T>) -> Self {
141        let ty = Vec::<T>::value_type();
142        let values = values.into_iter().map(Into::into);
143        Value::make_list(&ty, values).unwrap()
144    }
145}
146
147impl<T: ValueTyped> ValueTyped for Option<T> {
148    fn value_type() -> Type {
149        Type::option(T::value_type())
150    }
151}
152
153impl<T: ValueTyped + Into<Value>> From<Option<T>> for Value {
154    fn from(value: Option<T>) -> Self {
155        let ty = Option::<T>::value_type();
156        Value::make_option(&ty, value.map(Into::into)).unwrap()
157    }
158}
159
160impl<T: ValueTyped, U: ValueTyped> ValueTyped for Result<T, U> {
161    fn value_type() -> Type {
162        Type::result(Some(T::value_type()), Some(U::value_type()))
163    }
164}
165
166impl<T: ValueTyped + Into<Value>, U: ValueTyped + Into<Value>> From<Result<T, U>> for Value {
167    fn from(value: Result<T, U>) -> Self {
168        let ty = Result::<T, U>::value_type();
169        let value = match value {
170            Ok(ok) => Ok(Some(ok.into())),
171            Err(err) => Err(Some(err.into())),
172        };
173        Value::make_result(&ty, value).unwrap()
174    }
175}
176
177macro_rules! impl_tuple {
178    ($(($($var:ident),*)),*) => {
179        $(
180            impl<$($var: ValueTyped),*> ValueTyped for ($($var),*,) {
181                fn value_type() -> Type {
182                    Type::tuple([$($var::value_type()),*].to_vec()).unwrap()
183                }
184            }
185
186            #[allow(non_snake_case)]
187            impl<$($var: ValueTyped + Into<Value>),*> From<($($var),*,)> for Value {
188                fn from(($($var),*,): ($($var),*,)) -> Value {
189                    let ty = <($($var),*,)>::value_type();
190                    $(
191                        let $var = $var.into();
192                    )*
193                    Value::make_tuple(&ty, [$($var),*].to_vec()).unwrap()
194                }
195            }
196
197        )*
198    };
199}
200
201impl_tuple!(
202    (T1),
203    (T1, T2),
204    (T1, T2, T3),
205    (T1, T2, T3, T4),
206    (T1, T2, T3, T4, T5),
207    (T1, T2, T3, T4, T5, T6),
208    (T1, T2, T3, T4, T5, T6, T7),
209    (T1, T2, T3, T4, T5, T6, T7, T8),
210    (T1, T2, T3, T4, T5, T6, T7, T8, T9),
211    (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10),
212    (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11),
213    (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12),
214    (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13),
215    (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14),
216    (
217        T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15
218    ),
219    (
220        T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16
221    )
222);
223
224#[cfg(test)]
225mod tests {
226    use alloc::string::String;
227
228    use crate::value::{Type, Value};
229
230    #[test]
231    fn type_conversion_round_trips() {
232        for ty in [
233            Type::BOOL,
234            Type::U8,
235            Type::F32,
236            Type::STRING,
237            Type::list(Type::BOOL),
238            Type::record([("a", Type::BOOL)]).unwrap(),
239            Type::tuple([Type::BOOL]).unwrap(),
240            Type::variant([("a", None), ("b", Some(Type::BOOL))]).unwrap(),
241            Type::enum_ty(["north", "south"]).unwrap(),
242            Type::option(Type::BOOL),
243            Type::result(Some(Type::BOOL), None),
244            Type::flags(["read", "write"]).unwrap(),
245            Type::map(Type::STRING, Type::list(Type::BOOL)).unwrap(),
246        ] {
247            let got = Type::from_wasm_type(&ty).unwrap();
248            assert_eq!(got, ty);
249        }
250    }
251
252    #[test]
253    fn value_conversions() {
254        for (val, expect) in [
255            (1u8.into(), "1"),
256            ((-123i8).into(), "-123"),
257            (f32::NAN.into(), "nan"),
258            (f64::NEG_INFINITY.into(), "-inf"),
259            ('x'.into(), "'x'"),
260            ("str".into(), "\"str\""),
261            ([1, 2, 3].to_vec().into(), "[1, 2, 3]"),
262            ([1, 2, 3].into(), "[1, 2, 3]"),
263            (['a'; 3].into(), "['a', 'a', 'a']"),
264            (Some(1).into(), "some(1)"),
265            (None::<u8>.into(), "none"),
266            (Ok::<u8, String>(1).into(), "ok(1)"),
267            (Err::<u8, String>("oops".into()).into(), "err(\"oops\")"),
268            ((1,).into(), "(1)"),
269            ((1, "str", [9; 2]).into(), "(1, \"str\", [9, 9])"),
270        ] {
271            let val: Value = val;
272            let got = crate::to_string(&val).unwrap();
273            assert_eq!(got, expect);
274        }
275    }
276}