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}