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        crate::registry::persist_atomically(path, |temp_path| {
59            let file = std::fs::File::create(temp_path)?;
60            self.write_to(file, mode, session)
61        })
62    }
63}
64
65/// A trait that provides a subset of the [std::io::Write] functionality that is usable in no-std
66/// contexts.
67pub trait Writer {
68    fn write_fmt(&mut self, fmt: core::fmt::Arguments<'_>) -> anyhow::Result<()>;
69    fn write_all(&mut self, buf: &[u8]) -> anyhow::Result<()>;
70}
71
72#[cfg(feature = "std")]
73impl<W: ?Sized + std::io::Write> Writer for W {
74    fn write_fmt(&mut self, fmt: core::fmt::Arguments<'_>) -> anyhow::Result<()> {
75        <W as std::io::Write>::write_fmt(self, fmt).map_err(|err| err.into())
76    }
77
78    fn write_all(&mut self, buf: &[u8]) -> anyhow::Result<()> {
79        <W as std::io::Write>::write_all(self, buf).map_err(|err| err.into())
80    }
81}
82
83#[cfg(not(feature = "std"))]
84impl Writer for alloc::vec::Vec<u8> {
85    fn write_fmt(&mut self, fmt: core::fmt::Arguments<'_>) -> anyhow::Result<()> {
86        if let Some(s) = fmt.as_str() {
87            self.extend(s.as_bytes());
88        } else {
89            let formatted = fmt.to_string();
90            self.extend(formatted.as_bytes());
91        }
92        Ok(())
93    }
94
95    fn write_all(&mut self, buf: &[u8]) -> anyhow::Result<()> {
96        self.extend(buf);
97        Ok(())
98    }
99}
100
101#[cfg(not(feature = "std"))]
102impl Writer for alloc::string::String {
103    fn write_fmt(&mut self, fmt: core::fmt::Arguments<'_>) -> anyhow::Result<()> {
104        if let Some(s) = fmt.as_str() {
105            self.push_str(s);
106        } else {
107            let formatted = fmt.to_string();
108            self.push_str(&formatted);
109        }
110        Ok(())
111    }
112
113    fn write_all(&mut self, buf: &[u8]) -> anyhow::Result<()> {
114        let s = core::str::from_utf8(buf)?;
115        self.push_str(s);
116        Ok(())
117    }
118}
119
120impl<T: Emit> Emit for &T {
121    #[inline]
122    fn name(&self) -> Option<Symbol> {
123        (**self).name()
124    }
125
126    #[inline]
127    fn output_type(&self, mode: OutputMode) -> OutputType {
128        (**self).output_type(mode)
129    }
130
131    #[inline]
132    fn write_to<W: Writer>(
133        &self,
134        writer: W,
135        mode: OutputMode,
136        session: &Session,
137    ) -> anyhow::Result<()> {
138        (**self).write_to(writer, mode, session)
139    }
140}
141
142impl<T: Emit> Emit for &mut T {
143    #[inline]
144    fn name(&self) -> Option<Symbol> {
145        (**self).name()
146    }
147
148    #[inline]
149    fn output_type(&self, mode: OutputMode) -> OutputType {
150        (**self).output_type(mode)
151    }
152
153    #[inline]
154    fn write_to<W: Writer>(
155        &self,
156        writer: W,
157        mode: OutputMode,
158        session: &Session,
159    ) -> anyhow::Result<()> {
160        (**self).write_to(writer, mode, session)
161    }
162}
163
164impl<T: Emit> Emit for Box<T> {
165    #[inline]
166    fn name(&self) -> Option<Symbol> {
167        (**self).name()
168    }
169
170    #[inline]
171    fn output_type(&self, mode: OutputMode) -> OutputType {
172        (**self).output_type(mode)
173    }
174
175    #[inline]
176    fn write_to<W: Writer>(
177        &self,
178        writer: W,
179        mode: OutputMode,
180        session: &Session,
181    ) -> anyhow::Result<()> {
182        (**self).write_to(writer, mode, session)
183    }
184}
185
186impl<T: Emit> Emit for Arc<T> {
187    #[inline]
188    fn name(&self) -> Option<Symbol> {
189        (**self).name()
190    }
191
192    #[inline]
193    fn output_type(&self, mode: OutputMode) -> OutputType {
194        (**self).output_type(mode)
195    }
196
197    #[inline]
198    fn write_to<W: Writer>(
199        &self,
200        writer: W,
201        mode: OutputMode,
202        session: &Session,
203    ) -> anyhow::Result<()> {
204        (**self).write_to(writer, mode, session)
205    }
206}
207
208impl Emit for alloc::string::String {
209    fn name(&self) -> Option<Symbol> {
210        None
211    }
212
213    fn output_type(&self, _mode: OutputMode) -> OutputType {
214        OutputType::Hir
215    }
216
217    fn write_to<W: Writer>(
218        &self,
219        mut writer: W,
220        _mode: OutputMode,
221        _session: &Session,
222    ) -> anyhow::Result<()> {
223        writer.write_fmt(format_args!("{self}\n"))
224    }
225}
226
227impl Emit for miden_assembly_syntax::ast::Module {
228    fn name(&self) -> Option<Symbol> {
229        Some(Symbol::intern(self.path().to_string()))
230    }
231
232    fn output_type(&self, _mode: OutputMode) -> OutputType {
233        OutputType::Masm
234    }
235
236    fn write_to<W: Writer>(
237        &self,
238        mut writer: W,
239        mode: OutputMode,
240        _session: &Session,
241    ) -> anyhow::Result<()> {
242        assert_eq!(mode, OutputMode::Text, "masm syntax trees do not support binary mode");
243        writer.write_fmt(format_args!("{self}\n"))
244    }
245}
246
247impl Emit for miden_mast_package::Package {
248    fn name(&self) -> Option<Symbol> {
249        Some(Symbol::intern(&self.name))
250    }
251
252    fn output_type(&self, mode: OutputMode) -> OutputType {
253        match mode {
254            OutputMode::Text => OutputType::Mast,
255            OutputMode::Binary => OutputType::Masp,
256        }
257    }
258
259    fn write_to<W: Writer>(
260        &self,
261        mut writer: W,
262        mode: OutputMode,
263        _session: &Session,
264    ) -> anyhow::Result<()> {
265        use miden_core::{mast::MastNodeExt, serde::Serializable};
266        use miden_mast_package::PackageExport;
267        match mode {
268            OutputMode::Text => {
269                writer.write_fmt(format_args!("# package: {}@{}\n", self.name, self.version))?;
270                writer.write_fmt(format_args!("# kind:    {}\n\n", self.kind))?;
271                let forest = self.mast_forest();
272                for export in self.manifest.exports() {
273                    if let PackageExport::Procedure(proc) = export
274                        && let Some(node_id) = self.get_export_node(proc)
275                    {
276                        let node = forest[node_id].to_display(forest);
277                        writer.write_fmt(format_args!("# {}\n\n{node}\n\n", proc.path))?;
278                    }
279                }
280                Ok(())
281            }
282            OutputMode::Binary => {
283                let bytes = self.to_bytes();
284                writer.write_all(bytes.as_slice())
285            }
286        }
287    }
288}