calyx_utils/
out_file.rs

1use std::{
2    fmt::Display,
3    io::{self, BufWriter},
4    path::PathBuf,
5    str::FromStr,
6};
7
8/// Possible choices for output streams. Used by the `-o` option to the compiler.
9/// * "-" and "<out>" are treated as stdout.
10/// * "<err>" is treated as stderr.
11/// * "<null>" is treated as a null output stream.
12/// * All other strings are treated as file paths.
13#[derive(Debug, Clone)]
14pub enum OutputFile {
15    Null,
16    Stdout,
17    Stderr,
18    File {
19        path: PathBuf,
20        // Has the writer been initialized?
21        init: bool,
22    },
23}
24
25impl OutputFile {
26    pub fn file(path: PathBuf) -> Self {
27        OutputFile::File { path, init: false }
28    }
29
30    pub fn as_path_string(&self) -> String {
31        match self {
32            OutputFile::Null => "<null>".to_string(),
33            OutputFile::Stdout => "<stdout>".to_string(),
34            OutputFile::Stderr => "<stderr>".to_string(),
35            OutputFile::File { path, .. } => path.to_string_lossy().to_string(),
36        }
37    }
38}
39
40impl FromStr for OutputFile {
41    type Err = String;
42    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
43        match s {
44            "-" | "<out>" => Ok(OutputFile::Stdout),
45            "<err>" => Ok(OutputFile::Stderr),
46            "<null>" => Ok(OutputFile::Null),
47            _ => Ok(OutputFile::file(PathBuf::from(s))),
48        }
49    }
50}
51
52impl Display for OutputFile {
53    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54        let string = match self {
55            OutputFile::Stdout => "-",
56            OutputFile::Stderr => "<err>",
57            OutputFile::Null => "<null>",
58            OutputFile::File { path, .. } => path.to_str().unwrap(),
59        };
60
61        string.fmt(f)
62    }
63}
64
65impl OutputFile {
66    pub fn isatty(&self) -> bool {
67        match self {
68            OutputFile::Stdout => atty::is(atty::Stream::Stdout),
69            OutputFile::Stderr => atty::is(atty::Stream::Stderr),
70            OutputFile::Null | OutputFile::File { .. } => false,
71        }
72    }
73
74    pub fn get_write(&mut self) -> Box<dyn io::Write> {
75        match self {
76            OutputFile::Stdout => Box::new(BufWriter::new(std::io::stdout())),
77            OutputFile::Stderr => Box::new(BufWriter::new(std::io::stderr())),
78            OutputFile::File { path, init } => {
79                // If the file does not exist, create it. Otherwise, open it for appending.
80                let buf = if *init {
81                    assert!(
82                        path.exists(),
83                        "writer initialized but file does not exist"
84                    );
85                    BufWriter::new(
86                        std::fs::OpenOptions::new()
87                            .append(true)
88                            .open(path)
89                            .unwrap(),
90                    )
91                } else {
92                    *init = true;
93                    BufWriter::new(std::fs::File::create(path).unwrap())
94                };
95                Box::new(buf)
96            }
97            OutputFile::Null => Box::new(io::sink()),
98        }
99    }
100}