1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
use std::fmt;
use std::io;
use termcolor::{Color, ColorSpec};

/// Trait used to print WebAssembly modules in this crate.
///
/// Instances of this trait are passed to
/// [`Config::print`](super::Config::print). Instances of this trait are where
/// the output of a WebAssembly binary goes.
///
/// Note that this trait has built-in adapters in the `wasmprinter` crate:
///
/// * For users of [`std::io::Write`] use [`PrintIoWrite`].
/// * For users of [`std::fmt::Write`] use [`PrintFmtWrite`].
pub trait Print {
    /// Writes the given string `s` in its entirety.
    ///
    /// Returns an error for any I/O error.
    fn write_str(&mut self, s: &str) -> io::Result<()>;

    /// Indicates that a newline is being printed.
    ///
    /// This can be overridden to hook into the offset at which lines are
    /// printed.
    fn newline(&mut self) -> io::Result<()> {
        self.write_str("\n")
    }

    /// Indicates that a new line in the output is starting at the
    /// `binary_offset` provided.
    ///
    /// Not all new lines have a binary offset associated with them but this
    /// method should be called for new lines in the output. This enables
    /// correlating binary offsets to lines in the output.
    fn start_line(&mut self, binary_offset: Option<usize>) {
        let _ = binary_offset;
    }

    /// Enables usage of `write!` with this trait.
    fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> {
        struct Adapter<'a, T: ?Sized + 'a> {
            inner: &'a mut T,
            error: io::Result<()>,
        }

        impl<T: Print + ?Sized> fmt::Write for Adapter<'_, T> {
            fn write_str(&mut self, s: &str) -> fmt::Result {
                match self.inner.write_str(s) {
                    Ok(()) => Ok(()),
                    Err(e) => {
                        self.error = Err(e);
                        Err(fmt::Error)
                    }
                }
            }
        }

        let mut output = Adapter {
            inner: self,
            error: Ok(()),
        };
        match fmt::write(&mut output, args) {
            Ok(()) => Ok(()),
            Err(..) => output.error,
        }
    }

    /// Helper to print a custom section, if needed.
    ///
    /// If this output has a custom means of printing a custom section then this
    /// can be used to override the default. If `Ok(true)` is returned then the
    /// section will be considered to be printed. Otherwise an `Ok(false)`
    /// return value indicates that the default printing should happen.
    fn print_custom_section(
        &mut self,
        name: &str,
        binary_offset: usize,
        data: &[u8],
    ) -> io::Result<bool> {
        let _ = (name, binary_offset, data);
        Ok(false)
    }

    /// Sets the colors settings for literals (`"foo"`) to be printed.
    fn start_literal(&mut self) -> io::Result<()> {
        Ok(())
    }

    /// Sets the colors settings for a name (`$foo`) to be printed.
    fn start_name(&mut self) -> io::Result<()> {
        Ok(())
    }

    /// Sets the colors settings for a keyword (`(module ...)`) to be printed.
    fn start_keyword(&mut self) -> io::Result<()> {
        Ok(())
    }

    /// Sets the colors settings for a type (`(param i32)`) to be printed.
    fn start_type(&mut self) -> io::Result<()> {
        Ok(())
    }

    /// Sets the colors settings for a comment (`;; ...`) to be printed.
    fn start_comment(&mut self) -> io::Result<()> {
        Ok(())
    }

    /// Resets colors settings to the default.
    fn reset_color(&mut self) -> io::Result<()> {
        Ok(())
    }

    /// Returns `true` if the device uses colors without interacting synchronously with a terminal (e.g. ANSI)
    fn supports_async_color(&self) -> bool {
        false
    }
}

/// An adapter between the [`std::io::Write`] trait and [`Print`].
pub struct PrintIoWrite<T>(pub T);

impl<T> Print for PrintIoWrite<T>
where
    T: io::Write,
{
    fn write_str(&mut self, s: &str) -> io::Result<()> {
        self.0.write_all(s.as_bytes())
    }
}

/// An adapter between the [`std::fmt::Write`] trait and [`Print`].
pub struct PrintFmtWrite<T>(pub T);

impl<T> Print for PrintFmtWrite<T>
where
    T: fmt::Write,
{
    fn write_str(&mut self, s: &str) -> io::Result<()> {
        match self.0.write_str(s) {
            Ok(()) => Ok(()),
            Err(fmt::Error) => Err(io::Error::new(
                io::ErrorKind::Other,
                "failed to write string",
            )),
        }
    }
}

/// An adapter between the [`std::fmt::Write`] trait and [`termcolor::WriteColor`].
pub struct PrintTermcolor<T>(pub T);

impl<T> Print for PrintTermcolor<T>
where
    T: termcolor::WriteColor,
{
    fn write_str(&mut self, s: &str) -> io::Result<()> {
        self.0.write_all(s.as_bytes())
    }

    fn start_name(&mut self) -> io::Result<()> {
        self.0
            .set_color(ColorSpec::new().set_fg(Some(Color::Magenta)))
    }

    fn start_literal(&mut self) -> io::Result<()> {
        self.0.set_color(ColorSpec::new().set_fg(Some(Color::Red)))
    }

    fn start_keyword(&mut self) -> io::Result<()> {
        self.0.set_color(
            ColorSpec::new()
                .set_fg(Some(Color::Yellow))
                .set_bold(true)
                .set_intense(true),
        )
    }

    fn start_type(&mut self) -> io::Result<()> {
        self.0.set_color(
            ColorSpec::new()
                .set_fg(Some(Color::Green))
                .set_bold(true)
                .set_intense(true),
        )
    }

    fn start_comment(&mut self) -> io::Result<()> {
        self.0.set_color(ColorSpec::new().set_fg(Some(Color::Cyan)))
    }

    fn reset_color(&mut self) -> io::Result<()> {
        self.0.reset()
    }

    fn supports_async_color(&self) -> bool {
        self.0.supports_color() && !self.0.is_synchronous()
    }
}