wasm_wave/
writer.rs

1//! WAVE writer
2
3use std::{fmt::Debug, io::Write};
4
5use thiserror::Error;
6
7use crate::{
8    lex::Keyword,
9    wasm::{WasmTypeKind, WasmValue},
10};
11
12/// A Web Assembly Value Encoding writer.
13///
14/// Writes to the wrapped `W` writer.
15pub struct Writer<W> {
16    inner: W,
17}
18
19impl<W: Write> Writer<W> {
20    /// Returns a new Writer for the given [`std::io::Write`].
21    pub fn new(w: W) -> Self {
22        Self { inner: w }
23    }
24
25    /// WAVE-encodes and writes the given [`WasmValue`] to the underlying writer.
26    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") // Display is "NaN"
44                } 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") // Display is "NaN"
52                } 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/// A Writer error.
194#[derive(Debug, Error)]
195#[non_exhaustive]
196pub enum WriterError {
197    /// An error from the underlying writer
198    #[error("write failed: {0}")]
199    Io(#[from] std::io::Error),
200}