Skip to main content

midenc_session/
emit.rs

1use alloc::{boxed::Box, string::ToString, sync::Arc};
2
3use midenc_hir_symbol::Symbol;
4
5use crate::{OutputMode, OutputType, Session};
6
7pub trait Emit {
8    /// The name of this item, if applicable
9    fn name(&self) -> Option<Symbol>;
10    /// The output type associated with this item and the given `mode`
11    fn output_type(&self, mode: OutputMode) -> OutputType;
12    /// Write this item to the given [std::io::Write] handle, using `mode` to determine the output
13    /// type
14    fn write_to<W: Writer>(
15        &self,
16        writer: W,
17        mode: OutputMode,
18        _session: &Session,
19    ) -> anyhow::Result<()>;
20}
21
22#[cfg(feature = "std")]
23pub trait EmitExt: Emit {
24    /// Write this item to standard output, inferring the best [OutputMode] based on whether or not
25    /// stdout is a tty or not
26    fn write_to_stdout(&self, session: &Session) -> anyhow::Result<()>;
27    /// Write this item to the given file path, using `mode` to determine the output type
28    fn write_to_file(
29        &self,
30        path: &std::path::Path,
31        mode: OutputMode,
32        session: &Session,
33    ) -> anyhow::Result<()>;
34}
35
36#[cfg(feature = "std")]
37impl<T: ?Sized + Emit> EmitExt for T {
38    default fn write_to_stdout(&self, session: &Session) -> anyhow::Result<()> {
39        use std::io::IsTerminal;
40        let stdout = std::io::stdout().lock();
41        let mode = if stdout.is_terminal() {
42            OutputMode::Text
43        } else {
44            OutputMode::Binary
45        };
46        self.write_to(stdout, mode, session)
47    }
48
49    default fn write_to_file(
50        &self,
51        path: &std::path::Path,
52        mode: OutputMode,
53        session: &Session,
54    ) -> anyhow::Result<()> {
55        if let Some(dir) = path.parent() {
56            std::fs::create_dir_all(dir)?;
57        }
58        let file = std::fs::File::create(path)?;
59        self.write_to(file, mode, session)
60    }
61}
62
63/// A trait that provides a subset of the [std::io::Write] functionality that is usable in no-std
64/// contexts.
65pub trait Writer {
66    fn write_fmt(&mut self, fmt: core::fmt::Arguments<'_>) -> anyhow::Result<()>;
67    fn write_all(&mut self, buf: &[u8]) -> anyhow::Result<()>;
68}
69
70#[cfg(feature = "std")]
71impl<W: ?Sized + std::io::Write> Writer for W {
72    fn write_fmt(&mut self, fmt: core::fmt::Arguments<'_>) -> anyhow::Result<()> {
73        <W as std::io::Write>::write_fmt(self, fmt).map_err(|err| err.into())
74    }
75
76    fn write_all(&mut self, buf: &[u8]) -> anyhow::Result<()> {
77        <W as std::io::Write>::write_all(self, buf).map_err(|err| err.into())
78    }
79}
80
81#[cfg(not(feature = "std"))]
82impl Writer for alloc::vec::Vec<u8> {
83    fn write_fmt(&mut self, fmt: core::fmt::Arguments<'_>) -> anyhow::Result<()> {
84        if let Some(s) = fmt.as_str() {
85            self.extend(s.as_bytes());
86        } else {
87            let formatted = fmt.to_string();
88            self.extend(formatted.as_bytes());
89        }
90        Ok(())
91    }
92
93    fn write_all(&mut self, buf: &[u8]) -> anyhow::Result<()> {
94        self.extend(buf);
95        Ok(())
96    }
97}
98
99#[cfg(not(feature = "std"))]
100impl Writer for alloc::string::String {
101    fn write_fmt(&mut self, fmt: core::fmt::Arguments<'_>) -> anyhow::Result<()> {
102        if let Some(s) = fmt.as_str() {
103            self.push_str(s);
104        } else {
105            let formatted = fmt.to_string();
106            self.push_str(&formatted);
107        }
108        Ok(())
109    }
110
111    fn write_all(&mut self, buf: &[u8]) -> anyhow::Result<()> {
112        let s = core::str::from_utf8(buf)?;
113        self.push_str(s);
114        Ok(())
115    }
116}
117
118impl<T: Emit> Emit for &T {
119    #[inline]
120    fn name(&self) -> Option<Symbol> {
121        (**self).name()
122    }
123
124    #[inline]
125    fn output_type(&self, mode: OutputMode) -> OutputType {
126        (**self).output_type(mode)
127    }
128
129    #[inline]
130    fn write_to<W: Writer>(
131        &self,
132        writer: W,
133        mode: OutputMode,
134        session: &Session,
135    ) -> anyhow::Result<()> {
136        (**self).write_to(writer, mode, session)
137    }
138}
139
140impl<T: Emit> Emit for &mut T {
141    #[inline]
142    fn name(&self) -> Option<Symbol> {
143        (**self).name()
144    }
145
146    #[inline]
147    fn output_type(&self, mode: OutputMode) -> OutputType {
148        (**self).output_type(mode)
149    }
150
151    #[inline]
152    fn write_to<W: Writer>(
153        &self,
154        writer: W,
155        mode: OutputMode,
156        session: &Session,
157    ) -> anyhow::Result<()> {
158        (**self).write_to(writer, mode, session)
159    }
160}
161
162impl<T: Emit> Emit for Box<T> {
163    #[inline]
164    fn name(&self) -> Option<Symbol> {
165        (**self).name()
166    }
167
168    #[inline]
169    fn output_type(&self, mode: OutputMode) -> OutputType {
170        (**self).output_type(mode)
171    }
172
173    #[inline]
174    fn write_to<W: Writer>(
175        &self,
176        writer: W,
177        mode: OutputMode,
178        session: &Session,
179    ) -> anyhow::Result<()> {
180        (**self).write_to(writer, mode, session)
181    }
182}
183
184impl<T: Emit> Emit for Arc<T> {
185    #[inline]
186    fn name(&self) -> Option<Symbol> {
187        (**self).name()
188    }
189
190    #[inline]
191    fn output_type(&self, mode: OutputMode) -> OutputType {
192        (**self).output_type(mode)
193    }
194
195    #[inline]
196    fn write_to<W: Writer>(
197        &self,
198        writer: W,
199        mode: OutputMode,
200        session: &Session,
201    ) -> anyhow::Result<()> {
202        (**self).write_to(writer, mode, session)
203    }
204}
205
206impl Emit for alloc::string::String {
207    fn name(&self) -> Option<Symbol> {
208        None
209    }
210
211    fn output_type(&self, _mode: OutputMode) -> OutputType {
212        OutputType::Hir
213    }
214
215    fn write_to<W: Writer>(
216        &self,
217        mut writer: W,
218        _mode: OutputMode,
219        _session: &Session,
220    ) -> anyhow::Result<()> {
221        writer.write_fmt(format_args!("{self}\n"))
222    }
223}
224
225impl Emit for miden_assembly_syntax::ast::Module {
226    fn name(&self) -> Option<Symbol> {
227        Some(Symbol::intern(self.path().to_string()))
228    }
229
230    fn output_type(&self, _mode: OutputMode) -> OutputType {
231        OutputType::Masm
232    }
233
234    fn write_to<W: Writer>(
235        &self,
236        mut writer: W,
237        mode: OutputMode,
238        _session: &Session,
239    ) -> anyhow::Result<()> {
240        assert_eq!(mode, OutputMode::Text, "masm syntax trees do not support binary mode");
241        writer.write_fmt(format_args!("{self}\n"))
242    }
243}
244
245impl Emit for miden_mast_package::Package {
246    fn name(&self) -> Option<Symbol> {
247        Some(Symbol::intern(&self.name))
248    }
249
250    fn output_type(&self, mode: OutputMode) -> OutputType {
251        match mode {
252            OutputMode::Text => OutputType::Mast,
253            OutputMode::Binary => OutputType::Masp,
254        }
255    }
256
257    fn write_to<W: Writer>(
258        &self,
259        mut writer: W,
260        mode: OutputMode,
261        _session: &Session,
262    ) -> anyhow::Result<()> {
263        use miden_core::serde::Serializable;
264        match mode {
265            OutputMode::Text => {
266                let bytes = self.to_bytes();
267                writer.write_all(bytes.as_slice())
268            }
269            OutputMode::Binary => {
270                let bytes = self.to_bytes();
271                writer.write_all(bytes.as_slice())
272            }
273        }
274    }
275}