grep_cli/
wtr.rs

1use std::io::{self, IsTerminal};
2
3use termcolor::HyperlinkSpec;
4
5/// A writer that supports coloring with either line or block buffering.
6#[derive(Debug)]
7pub struct StandardStream(StandardStreamKind);
8
9/// Returns a possibly buffered writer to stdout for the given color choice.
10///
11/// The writer returned is either line buffered or block buffered. The decision
12/// between these two is made automatically based on whether a tty is attached
13/// to stdout or not. If a tty is attached, then line buffering is used.
14/// Otherwise, block buffering is used. In general, block buffering is more
15/// efficient, but may increase the time it takes for the end user to see the
16/// first bits of output.
17///
18/// If you need more fine grained control over the buffering mode, then use one
19/// of `stdout_buffered_line` or `stdout_buffered_block`.
20///
21/// The color choice given is passed along to the underlying writer. To
22/// completely disable colors in all cases, use `ColorChoice::Never`.
23pub fn stdout(color_choice: termcolor::ColorChoice) -> StandardStream {
24    if std::io::stdout().is_terminal() {
25        stdout_buffered_line(color_choice)
26    } else {
27        stdout_buffered_block(color_choice)
28    }
29}
30
31/// Returns a line buffered writer to stdout for the given color choice.
32///
33/// This writer is useful when printing results directly to a tty such that
34/// users see output as soon as it's written. The downside of this approach
35/// is that it can be slower, especially when there is a lot of output.
36///
37/// You might consider using [`stdout`] instead, which chooses the buffering
38/// strategy automatically based on whether stdout is connected to a tty.
39pub fn stdout_buffered_line(
40    color_choice: termcolor::ColorChoice,
41) -> StandardStream {
42    let out = termcolor::StandardStream::stdout(color_choice);
43    StandardStream(StandardStreamKind::LineBuffered(out))
44}
45
46/// Returns a block buffered writer to stdout for the given color choice.
47///
48/// This writer is useful when printing results to a file since it amortizes
49/// the cost of writing data. The downside of this approach is that it can
50/// increase the latency of display output when writing to a tty.
51///
52/// You might consider using [`stdout`] instead, which chooses the buffering
53/// strategy automatically based on whether stdout is connected to a tty.
54pub fn stdout_buffered_block(
55    color_choice: termcolor::ColorChoice,
56) -> StandardStream {
57    let out = termcolor::BufferedStandardStream::stdout(color_choice);
58    StandardStream(StandardStreamKind::BlockBuffered(out))
59}
60
61#[derive(Debug)]
62enum StandardStreamKind {
63    LineBuffered(termcolor::StandardStream),
64    BlockBuffered(termcolor::BufferedStandardStream),
65}
66
67impl io::Write for StandardStream {
68    #[inline]
69    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
70        use self::StandardStreamKind::*;
71
72        match self.0 {
73            LineBuffered(ref mut w) => w.write(buf),
74            BlockBuffered(ref mut w) => w.write(buf),
75        }
76    }
77
78    #[inline]
79    fn flush(&mut self) -> io::Result<()> {
80        use self::StandardStreamKind::*;
81
82        match self.0 {
83            LineBuffered(ref mut w) => w.flush(),
84            BlockBuffered(ref mut w) => w.flush(),
85        }
86    }
87}
88
89impl termcolor::WriteColor for StandardStream {
90    #[inline]
91    fn supports_color(&self) -> bool {
92        use self::StandardStreamKind::*;
93
94        match self.0 {
95            LineBuffered(ref w) => w.supports_color(),
96            BlockBuffered(ref w) => w.supports_color(),
97        }
98    }
99
100    #[inline]
101    fn supports_hyperlinks(&self) -> bool {
102        use self::StandardStreamKind::*;
103
104        match self.0 {
105            LineBuffered(ref w) => w.supports_hyperlinks(),
106            BlockBuffered(ref w) => w.supports_hyperlinks(),
107        }
108    }
109
110    #[inline]
111    fn set_color(&mut self, spec: &termcolor::ColorSpec) -> io::Result<()> {
112        use self::StandardStreamKind::*;
113
114        match self.0 {
115            LineBuffered(ref mut w) => w.set_color(spec),
116            BlockBuffered(ref mut w) => w.set_color(spec),
117        }
118    }
119
120    #[inline]
121    fn set_hyperlink(&mut self, link: &HyperlinkSpec) -> io::Result<()> {
122        use self::StandardStreamKind::*;
123
124        match self.0 {
125            LineBuffered(ref mut w) => w.set_hyperlink(link),
126            BlockBuffered(ref mut w) => w.set_hyperlink(link),
127        }
128    }
129
130    #[inline]
131    fn reset(&mut self) -> io::Result<()> {
132        use self::StandardStreamKind::*;
133
134        match self.0 {
135            LineBuffered(ref mut w) => w.reset(),
136            BlockBuffered(ref mut w) => w.reset(),
137        }
138    }
139
140    #[inline]
141    fn is_synchronous(&self) -> bool {
142        use self::StandardStreamKind::*;
143
144        match self.0 {
145            LineBuffered(ref w) => w.is_synchronous(),
146            BlockBuffered(ref w) => w.is_synchronous(),
147        }
148    }
149}