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 fn name(&self) -> Option<Symbol>;
10 fn output_type(&self, mode: OutputMode) -> OutputType;
12 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 fn write_to_stdout(&self, session: &Session) -> anyhow::Result<()>;
27 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
65pub 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}