Skip to main content

lisette_emit/
output.rs

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}