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::Record => {
79 self.write_str("{")?;
80 let mut first = true;
81 for (name, val) in val.unwrap_record() {
82 if !matches!(val.kind(), WasmTypeKind::Option) || val.unwrap_option().is_some()
83 {
84 if first {
85 first = false;
86 } else {
87 self.write_str(", ")?;
88 }
89 self.write_str(name)?;
90 self.write_str(": ")?;
91 self.write_value(&*val)?;
92 }
93 }
94 if first {
95 self.write_str(":")?;
96 }
97 self.write_str("}")
98 }
99 WasmTypeKind::Tuple => {
100 self.write_str("(")?;
101 for (idx, val) in val.unwrap_tuple().enumerate() {
102 if idx != 0 {
103 self.write_str(", ")?;
104 }
105 self.write_value(&*val)?;
106 }
107 self.write_str(")")
108 }
109 WasmTypeKind::Variant => {
110 let (name, val) = val.unwrap_variant();
111 if Keyword::decode(&name).is_some() {
112 self.write_char('%')?;
113 }
114 self.write_str(name)?;
115 if let Some(val) = val {
116 self.write_str("(")?;
117 self.write_value(&*val)?;
118 self.write_str(")")?;
119 }
120 Ok(())
121 }
122 WasmTypeKind::Enum => {
123 let case = val.unwrap_enum();
124 if Keyword::decode(&case).is_some() {
125 self.write_char('%')?;
126 }
127 self.write_str(case)
128 }
129 WasmTypeKind::Option => match val.unwrap_option() {
130 Some(val) => {
131 self.write_str("some(")?;
132 self.write_value(&*val)?;
133 self.write_str(")")
134 }
135 None => self.write_str("none"),
136 },
137 WasmTypeKind::Result => {
138 let (name, val) = match val.unwrap_result() {
139 Ok(val) => ("ok", val),
140 Err(val) => ("err", val),
141 };
142 self.write_str(name)?;
143 if let Some(val) = val {
144 self.write_str("(")?;
145 self.write_value(&*val)?;
146 self.write_str(")")?;
147 }
148 Ok(())
149 }
150 WasmTypeKind::Flags => {
151 self.write_str("{")?;
152 for (idx, name) in val.unwrap_flags().enumerate() {
153 if idx != 0 {
154 self.write_str(", ")?;
155 }
156 self.write_str(name)?;
157 }
158 self.write_str("}")?;
159 Ok(())
160 }
161 WasmTypeKind::Unsupported => panic!("unsupported value type"),
162 }
163 }
164
165 fn write_str(&mut self, s: impl AsRef<str>) -> Result<(), WriterError> {
166 self.inner.write_all(s.as_ref().as_bytes())?;
167 Ok(())
168 }
169
170 fn write_display(&mut self, d: impl std::fmt::Display) -> Result<(), WriterError> {
171 write!(self.inner, "{d}")?;
172 Ok(())
173 }
174
175 fn write_char(&mut self, ch: char) -> Result<(), WriterError> {
176 if "\\\"\'\t\r\n".contains(ch) {
177 write!(self.inner, "{}", ch.escape_default())?;
178 } else if ch.is_control() {
179 write!(self.inner, "{}", ch.escape_unicode())?;
180 } else {
181 write!(self.inner, "{}", ch.escape_debug())?;
182 }
183 Ok(())
184 }
185}
186
187impl<W> AsMut<W> for Writer<W> {
188 fn as_mut(&mut self) -> &mut W {
189 &mut self.inner
190 }
191}
192
193#[derive(Debug, Error)]
195#[non_exhaustive]
196pub enum WriterError {
197 #[error("write failed: {0}")]
199 Io(#[from] std::io::Error),
200}