1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
use crate::Stream;
use std::{fmt, fs, io, path};

/// The Output type
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Output {
    /// Standard Output
    Stdout,
    /// Standard Error
    Stderr,
    /// File Stream
    File(path::PathBuf),
}

impl fmt::Display for Output {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(
            fmt,
            "{}",
            match self {
                Self::Stdout => "<stdout>",
                Self::Stderr => "<stderr>",
                Self::File(path) => path.to_str().unwrap_or("<???>"),
            }
        )
    }
}

impl std::str::FromStr for Output {
    type Err = ();
    fn from_str(s: &str) -> Result<Self, ()> {
        Ok(match s {
            "stdout" | "Stdout" | "StdOut" | "STDOUT" | "<stdout>" => Self::Stdout,
            "stderr" | "Stderr" | "StdErr" | "STDERR" | "<stderr>" => Self::Stderr,
            path => Self::File(path.into()),
        })
    }
}

impl Default for Output {
    fn default() -> Self {
        Self::Stderr
    }
}

impl<T: Into<path::PathBuf>> From<T> for Output {
    fn from(path: T) -> Self {
        Output::File(path.into())
    }
}

fn new_file(path: path::PathBuf) -> io::Result<fs::File> {
    // Append to an existing file or create a new file.
    fs::OpenOptions::new()
        .append(true)
        .open(path.clone())
        .or(fs::File::create(path))
}

impl Output {
    /// Create `Stream` from `Output`.
    pub(crate) fn to_stream(&self) -> io::Result<Stream> {
        Ok(match self.clone() {
            Self::Stdout => Stream::from(io::stdout()),
            Self::Stderr => Stream::from(io::stderr()),
            Self::File(path) => Stream::from(new_file(path)?),
        })
    }
}