wasm_wave/value/
convert.rs

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