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 let file = std::fs::File::create(path)?;
59 self.write_to(file, mode, session)
60 }
61}
62
63pub 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}