1use rustc_hash::FxHashMap as HashMap;
2use std::io::{self, Write};
3use std::process::{Command, Stdio};
4
5use super::imports::format_import;
6
7#[derive(Clone, Debug)]
8pub struct OutputFile {
9 pub name: String,
10 pub source: String,
11 pub imports: HashMap<String, String>,
12 pub package_name: String,
13}
14
15impl OutputFile {
16 pub fn to_go(&self) -> String {
17 let unformatted = self.render_unformatted();
18 self.gofmt(&unformatted).unwrap_or(unformatted)
19 }
20
21 fn render_unformatted(&self) -> String {
22 let mut output = OutputCollector::new();
23
24 output.collect(format!("package {}", self.package_name));
25
26 match self.imports.len() {
27 0 => {}
28 1 => {
29 let (path, alias) = self
30 .imports
31 .iter()
32 .next()
33 .expect("single-element collection must have first element");
34 output.collect(format!("import {}", format_import(path, alias)));
35 }
36 _ => {
37 output.collect("import (");
38 for (path, alias) in &self.imports {
39 output.collect(format_import(path, alias));
40 }
41 output.collect(")");
42 }
43 }
44
45 output.collect(&self.source);
46 output.render()
47 }
48
49 fn gofmt(&self, code: &str) -> Result<String, io::Error> {
50 let mut child = Command::new("gofmt")
51 .stdin(Stdio::piped())
52 .stdout(Stdio::piped())
53 .stderr(Stdio::piped())
54 .spawn()?;
55
56 let Some(mut stdin) = child.stdin.take() else {
57 return Err(io::Error::other("Failed to open stdin"));
58 };
59
60 stdin.write_all(code.as_bytes())?;
61 drop(stdin);
62
63 let output = child.wait_with_output()?;
64
65 if !output.status.success() {
66 return Err(io::Error::other("gofmt failed"));
67 }
68
69 Ok(String::from_utf8_lossy(&output.stdout).into_owned())
70 }
71}
72
73#[derive(Default)]
74pub(crate) struct OutputCollector {
75 output: Vec<String>,
76}
77
78impl OutputCollector {
79 pub(crate) fn new() -> Self {
80 Self::default()
81 }
82
83 pub(crate) fn render(&self) -> String {
84 self.output.join("\n")
85 }
86
87 pub(crate) fn collect(&mut self, line: impl Into<String>) {
88 self.output.push(line.into());
89 }
90
91 pub(crate) fn collect_with_blank(&mut self, line: impl Into<String>) {
92 self.output.push(line.into());
93 self.output.push(String::new());
94 }
95}