1use std::{
2 fmt::Display,
3 io::{self, BufWriter},
4 path::PathBuf,
5 str::FromStr,
6};
7
8#[derive(Debug, Clone)]
14pub enum OutputFile {
15 Null,
16 Stdout,
17 Stderr,
18 File {
19 path: PathBuf,
20 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 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}