Skip to main content

wasm_wave/wasm/
fmt.rs

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
11/// Implements a WAVE-formatted [`Display`] for a [`WasmType`].
12pub 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
101/// Implements a WAVE-formatted [`Display`] for a [`WasmValue`].
102///
103/// Formatting fails with [`core::fmt::Error`] if the value can't be encoded as
104/// WAVE; use [`to_string`](crate::to_string) to get a
105/// [`WriterError`](crate::writer::WriterError) describing why instead.
106pub 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
116/// Implements a WAVE-formatted [`Display`] for a [`WasmFunc`].
117pub 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
163/// Implements a WAVE-formatted [`Display`] for [`WasmValue`] func arguments.
164pub 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
179/// Implements a WAVE-formatted [`Display`] for [`WasmValue`] func results.
180pub 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}