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