1use core::fmt::Display;
2
3use alloc::{string::ToString, vec::Vec};
4
5use crate::{
6 WasmValue,
7 wasm::{WasmFunc, WasmType, WasmTypeKind},
8 writer::Writer,
9};
10
11pub struct DisplayType<'a, T: WasmType>(pub &'a T);
13
14impl<T: WasmType> Display for DisplayType<'_, T> {
15 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
16 let ty = self.0;
17 match ty.kind() {
18 WasmTypeKind::List => {
19 write!(f, "list<{}>", DisplayType(&ty.list_element_type().unwrap()))
20 }
21 WasmTypeKind::Record => {
22 f.write_str("record { ")?;
23 for (idx, (name, field_type)) in ty.record_fields().enumerate() {
24 if idx != 0 {
25 f.write_str(", ")?;
26 }
27 write!(f, "{name}: {}", DisplayType(&field_type))?;
28 }
29 f.write_str(" }")
30 }
31 WasmTypeKind::Tuple => {
32 f.write_str("tuple<")?;
33 for (idx, ty) in ty.tuple_element_types().enumerate() {
34 if idx != 0 {
35 f.write_str(", ")?;
36 }
37 write!(f, "{}", DisplayType(&ty))?;
38 }
39 f.write_str(">")
40 }
41 WasmTypeKind::Variant => {
42 f.write_str("variant { ")?;
43 for (idx, (name, payload)) in ty.variant_cases().enumerate() {
44 if idx != 0 {
45 f.write_str(", ")?;
46 }
47 f.write_str(name.as_ref())?;
48 if let Some(ty) = payload {
49 write!(f, "({})", DisplayType(&ty))?;
50 }
51 }
52 f.write_str(" }")
53 }
54 WasmTypeKind::Enum => {
55 f.write_str("enum { ")?;
56 for (idx, name) in ty.enum_cases().enumerate() {
57 if idx != 0 {
58 f.write_str(", ")?;
59 }
60 f.write_str(name.as_ref())?;
61 }
62 f.write_str(" }")
63 }
64 WasmTypeKind::Option => {
65 write!(
66 f,
67 "option<{}>",
68 DisplayType(&ty.option_some_type().unwrap())
69 )
70 }
71 WasmTypeKind::Result => {
72 f.write_str("result")?;
73 match ty.result_types().unwrap() {
74 (None, None) => Ok(()),
75 (None, Some(err)) => write!(f, "<_, {}>", DisplayType(&err)),
76 (Some(ok), None) => write!(f, "<{}>", DisplayType(&ok)),
77 (Some(ok), Some(err)) => {
78 write!(f, "<{}, {}>", DisplayType(&ok), DisplayType(&err))
79 }
80 }
81 }
82 WasmTypeKind::Flags => {
83 f.write_str("flags { ")?;
84 for (idx, name) in ty.flags_names().enumerate() {
85 if idx != 0 {
86 f.write_str(", ")?;
87 }
88 f.write_str(name.as_ref())?;
89 }
90 f.write_str(" }")
91 }
92 WasmTypeKind::Map => {
93 let (key, value) = ty.map_key_value_types().unwrap();
94 write!(f, "map<{}, {}>", DisplayType(&key), DisplayType(&value))
95 }
96 simple => Display::fmt(&simple, f),
97 }
98 }
99}
100
101pub struct DisplayValue<'a, T: WasmValue>(pub &'a T);
107
108impl<T: WasmValue> Display for DisplayValue<'_, T> {
109 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
110 Writer::new(f)
111 .write_value(self.0)
112 .map_err(|_| core::fmt::Error)
113 }
114}
115
116pub struct DisplayFunc<T: WasmFunc>(pub T);
118
119impl<T: WasmFunc> Display for DisplayFunc<T> {
120 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
121 f.write_str("func(")?;
122 let mut param_names = self.0.param_names();
123 for (idx, ty) in self.0.params().enumerate() {
124 if idx != 0 {
125 f.write_str(", ")?;
126 }
127 if let Some(name) = param_names.next() {
128 write!(f, "{name}: ")?;
129 }
130 DisplayType(&ty).fmt(f)?
131 }
132 f.write_str(")")?;
133
134 let results = self.0.results().collect::<Vec<_>>();
135 if results.is_empty() {
136 return Ok(());
137 }
138
139 let mut result_names = self.0.result_names();
140 if results.len() == 1 {
141 let ty = DisplayType(&results.into_iter().next().unwrap()).to_string();
142 if let Some(name) = result_names.next() {
143 write!(f, " -> ({name}: {ty})")
144 } else {
145 write!(f, " -> {ty}")
146 }
147 } else {
148 f.write_str(" -> (")?;
149 for (idx, ty) in results.into_iter().enumerate() {
150 if idx != 0 {
151 f.write_str(", ")?;
152 }
153 if let Some(name) = result_names.next() {
154 write!(f, "{name}: ")?;
155 }
156 DisplayType(&ty).fmt(f)?;
157 }
158 f.write_str(")")
159 }
160 }
161}
162
163pub struct DisplayFuncArgs<'a, T: WasmValue>(pub &'a [T]);
165
166impl<T: WasmValue> Display for DisplayFuncArgs<'_, T> {
167 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
168 f.write_str("(")?;
169 for (idx, v) in self.0.iter().enumerate() {
170 if idx != 0 {
171 f.write_str(", ")?;
172 }
173 DisplayValue(v).fmt(f)?;
174 }
175 f.write_str(")")
176 }
177}
178
179pub struct DisplayFuncResults<'a, T: WasmValue>(pub &'a [T]);
181
182impl<T: WasmValue> Display for DisplayFuncResults<'_, T> {
183 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
184 if self.0.len() == 1 {
185 DisplayValue(&self.0[0]).fmt(f)
186 } else {
187 DisplayFuncArgs(self.0).fmt(f)
188 }
189 }
190}
191
192#[cfg(test)]
193mod tests {
194 use alloc::string::ToString;
195
196 use crate::value::Type;
197
198 #[test]
199 fn test_type_display() {
200 for (ty, expected) in [
201 (Type::U8, "u8"),
202 (Type::list(Type::U8), "list<u8>"),
203 (
204 Type::record([("a", Type::U8), ("b", Type::STRING)]).unwrap(),
205 "record { a: u8, b: string }",
206 ),
207 (
208 Type::tuple([Type::U8, Type::BOOL]).unwrap(),
209 "tuple<u8, bool>",
210 ),
211 (
212 Type::variant([("off", None), ("on", Some(Type::U8))]).unwrap(),
213 "variant { off, on(u8) }",
214 ),
215 (
216 Type::enum_ty(["east", "west"]).unwrap(),
217 "enum { east, west }",
218 ),
219 (Type::option(Type::U8), "option<u8>"),
220 (Type::result(None, None), "result"),
221 (Type::result(Some(Type::U8), None), "result<u8>"),
222 (Type::result(None, Some(Type::STRING)), "result<_, string>"),
223 (
224 Type::result(Some(Type::U8), Some(Type::STRING)),
225 "result<u8, string>",
226 ),
227 (
228 Type::flags(["read", "write"]).unwrap(),
229 "flags { read, write }",
230 ),
231 (
232 Type::map(Type::STRING, Type::U32).unwrap(),
233 "map<string, u32>",
234 ),
235 (
236 Type::map(
237 Type::CHAR,
238 Type::map(Type::BOOL, Type::list(Type::U8)).unwrap(),
239 )
240 .unwrap(),
241 "map<char, map<bool, list<u8>>>",
242 ),
243 ] {
244 assert_eq!(ty.to_string(), expected);
245 }
246 }
247}