1use std::{fmt::Debug, io::Write};
4
5use thiserror::Error;
6
7use crate::{
8 lex::Keyword,
9 wasm::{WasmTypeKind, WasmValue},
10};
11
12pub struct Writer<W> {
16 inner: W,
17}
18
19impl<W: Write> Writer<W> {
20 pub fn new(w: W) -> Self {
22 Self { inner: w }
23 }
24
25 pub fn write_value<V>(&mut self, val: &V) -> Result<(), WriterError>
27 where
28 V: WasmValue,
29 {
30 match val.kind() {
31 WasmTypeKind::Bool => self.write_str(if val.unwrap_bool() { "true" } else { "false" }),
32 WasmTypeKind::S8 => self.write_display(val.unwrap_s8()),
33 WasmTypeKind::S16 => self.write_display(val.unwrap_s16()),
34 WasmTypeKind::S32 => self.write_display(val.unwrap_s32()),
35 WasmTypeKind::S64 => self.write_display(val.unwrap_s64()),
36 WasmTypeKind::U8 => self.write_display(val.unwrap_u8()),
37 WasmTypeKind::U16 => self.write_display(val.unwrap_u16()),
38 WasmTypeKind::U32 => self.write_display(val.unwrap_u32()),
39 WasmTypeKind::U64 => self.write_display(val.unwrap_u64()),
40 WasmTypeKind::F32 => {
41 let f = val.unwrap_f32();
42 if f.is_nan() {
43 self.write_str("nan") } else {
45 self.write_display(f)
46 }
47 }
48 WasmTypeKind::F64 => {
49 let f = val.unwrap_f64();
50 if f.is_nan() {
51 self.write_str("nan") } else {
53 self.write_display(f)
54 }
55 }
56 WasmTypeKind::Char => {
57 self.write_str("'")?;
58 self.write_char(val.unwrap_char())?;
59 self.write_str("'")
60 }
61 WasmTypeKind::String => {
62 self.write_str("\"")?;
63 for ch in val.unwrap_string().chars() {
64 self.write_char(ch)?;
65 }
66 self.write_str("\"")
67 }
68 WasmTypeKind::List => {
69 self.write_str("[")?;
70 for (idx, val) in val.unwrap_list().enumerate() {
71 if idx != 0 {
72 self.write_str(", ")?;
73 }
74 self.write_value(&*val)?;
75 }
76 self.write_str("]")
77 }
78 WasmTypeKind::FixedSizeList => {
79 self.write_str("[")?;
80 for (idx, val) in val.unwrap_list().enumerate() {
81 if idx != 0 {
82 self.write_str(", ")?;
83 }
84 self.write_value(&*val)?;
85 }
86 self.write_str("]")
87 }
88 WasmTypeKind::Record => {
89 self.write_str("{")?;
90 let mut first = true;
91 for (name, val) in val.unwrap_record() {
92 if !matches!(val.kind(), WasmTypeKind::Option) || val.unwrap_option().is_some()
93 {
94 if first {
95 first = false;
96 } else {
97 self.write_str(", ")?;
98 }
99 self.write_str(name)?;
100 self.write_str(": ")?;
101 self.write_value(&*val)?;
102 }
103 }
104 if first {
105 self.write_str(":")?;
106 }
107 self.write_str("}")
108 }
109 WasmTypeKind::Tuple => {
110 self.write_str("(")?;
111 for (idx, val) in val.unwrap_tuple().enumerate() {
112 if idx != 0 {
113 self.write_str(", ")?;
114 }
115 self.write_value(&*val)?;
116 }
117 self.write_str(")")
118 }
119 WasmTypeKind::Variant => {
120 let (name, val) = val.unwrap_variant();
121 if Keyword::decode(&name).is_some() {
122 self.write_char('%')?;
123 }
124 self.write_str(name)?;
125 if let Some(val) = val {
126 self.write_str("(")?;
127 self.write_value(&*val)?;
128 self.write_str(")")?;
129 }
130 Ok(())
131 }
132 WasmTypeKind::Enum => {
133 let case = val.unwrap_enum();
134 if Keyword::decode(&case).is_some() {
135 self.write_char('%')?;
136 }
137 self.write_str(case)
138 }
139 WasmTypeKind::Option => match val.unwrap_option() {
140 Some(val) => {
141 self.write_str("some(")?;
142 self.write_value(&*val)?;
143 self.write_str(")")
144 }
145 None => self.write_str("none"),
146 },
147 WasmTypeKind::Result => {
148 let (name, val) = match val.unwrap_result() {
149 Ok(val) => ("ok", val),
150 Err(val) => ("err", val),
151 };
152 self.write_str(name)?;
153 if let Some(val) = val {
154 self.write_str("(")?;
155 self.write_value(&*val)?;
156 self.write_str(")")?;
157 }
158 Ok(())
159 }
160 WasmTypeKind::Flags => {
161 self.write_str("{")?;
162 for (idx, name) in val.unwrap_flags().enumerate() {
163 if idx != 0 {
164 self.write_str(", ")?;
165 }
166 self.write_str(name)?;
167 }
168 self.write_str("}")?;
169 Ok(())
170 }
171 WasmTypeKind::Unsupported => panic!("unsupported value type"),
172 }
173 }
174
175 fn write_str(&mut self, s: impl AsRef<str>) -> Result<(), WriterError> {
176 self.inner.write_all(s.as_ref().as_bytes())?;
177 Ok(())
178 }
179
180 fn write_display(&mut self, d: impl std::fmt::Display) -> Result<(), WriterError> {
181 write!(self.inner, "{d}")?;
182 Ok(())
183 }
184
185 fn write_char(&mut self, ch: char) -> Result<(), WriterError> {
186 if "\\\"\'\t\r\n".contains(ch) {
187 write!(self.inner, "{}", ch.escape_default())?;
188 } else if ch.is_control() {
189 write!(self.inner, "{}", ch.escape_unicode())?;
190 } else {
191 write!(self.inner, "{}", ch.escape_debug())?;
192 }
193 Ok(())
194 }
195}
196
197impl<W> AsMut<W> for Writer<W> {
198 fn as_mut(&mut self) -> &mut W {
199 &mut self.inner
200 }
201}
202
203#[derive(Debug, Error)]
205#[non_exhaustive]
206pub enum WriterError {
207 #[error("write failed: {0}")]
209 Io(#[from] std::io::Error),
210}