Skip to main content

lisette_emit/output/
mod.rs

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