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::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/// A Writer error.
204#[derive(Debug, Error)]
205#[non_exhaustive]
206pub enum WriterError {
207    /// An error from the underlying writer
208    #[error("write failed: {0}")]
209    Io(#[from] std::io::Error),
210}