/rust/registry/src/index.crates.io-1949cf8c6b5b557f/anstream-1.0.0/src/auto.rs
Line | Count | Source |
1 | | use crate::stream::AsLockedWrite; |
2 | | use crate::stream::RawStream; |
3 | | use crate::ColorChoice; |
4 | | use crate::StripStream; |
5 | | #[cfg(all(windows, feature = "wincon"))] |
6 | | use crate::WinconStream; |
7 | | |
8 | | /// [`std::io::Write`] that adapts ANSI escape codes to the underlying `Write`s capabilities |
9 | | /// |
10 | | /// This includes |
11 | | /// - Stripping colors for non-terminals |
12 | | /// - Respecting env variables like [NO_COLOR](https://no-color.org/) or [CLICOLOR](https://bixense.com/clicolors/) |
13 | | /// - *(windows)* Falling back to the wincon API where [ENABLE_VIRTUAL_TERMINAL_PROCESSING](https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#output-sequences) is unsupported |
14 | | /// |
15 | | /// You can customize auto-detection by calling into |
16 | | /// [anstyle_query](https://docs.rs/anstyle-query/latest/anstyle_query/) |
17 | | /// to get a [`ColorChoice`] and then calling [`AutoStream::new(stream, choice)`]. |
18 | | #[derive(Debug)] |
19 | | pub struct AutoStream<S: RawStream> { |
20 | | inner: StreamInner<S>, |
21 | | } |
22 | | |
23 | | #[derive(Debug)] |
24 | | enum StreamInner<S: RawStream> { |
25 | | PassThrough(S), |
26 | | Strip(StripStream<S>), |
27 | | #[cfg(all(windows, feature = "wincon"))] |
28 | | Wincon(WinconStream<S>), |
29 | | } |
30 | | |
31 | | impl<S> AutoStream<S> |
32 | | where |
33 | | S: RawStream, |
34 | | { |
35 | | /// Runtime control over styling behavior |
36 | | /// |
37 | | /// # Example |
38 | | /// |
39 | | /// ```rust |
40 | | /// # #[cfg(feature = "auto")] { |
41 | | /// # use std::io::IsTerminal as _; |
42 | | /// // Like `AutoStream::choice` but without `NO_COLOR`, `CLICOLOR_FORCE`, `CI` |
43 | | /// fn choice(raw: &dyn anstream::stream::RawStream) -> anstream::ColorChoice { |
44 | | /// let choice = anstream::ColorChoice::global(); |
45 | | /// if choice == anstream::ColorChoice::Auto { |
46 | | /// if raw.is_terminal() && anstyle_query::term_supports_color() { |
47 | | /// anstream::ColorChoice::Always |
48 | | /// } else { |
49 | | /// anstream::ColorChoice::Never |
50 | | /// } |
51 | | /// } else { |
52 | | /// choice |
53 | | /// } |
54 | | /// } |
55 | | /// |
56 | | /// let stream = std::io::stdout(); |
57 | | /// let choice = choice(&stream); |
58 | | /// let auto = anstream::AutoStream::new(stream, choice); |
59 | | /// # } |
60 | | /// ``` |
61 | | #[inline] |
62 | 0 | pub fn new(raw: S, choice: ColorChoice) -> Self { |
63 | 0 | match choice { |
64 | | #[cfg(feature = "auto")] |
65 | 0 | ColorChoice::Auto => Self::auto(raw), |
66 | | #[cfg(not(feature = "auto"))] |
67 | | ColorChoice::Auto => Self::never(raw), |
68 | 0 | ColorChoice::AlwaysAnsi => Self::always_ansi(raw), |
69 | 0 | ColorChoice::Always => Self::always(raw), |
70 | 0 | ColorChoice::Never => Self::never(raw), |
71 | | } |
72 | 0 | } Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::StderrLock>>::new Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::StdoutLock>>::new Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::Stderr>>::new Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::Stdout>>::new |
73 | | |
74 | | /// Auto-adapt for the stream's capabilities |
75 | | #[cfg(feature = "auto")] |
76 | | #[inline] |
77 | 0 | pub fn auto(raw: S) -> Self { |
78 | 0 | let choice = Self::choice(&raw); |
79 | 0 | debug_assert_ne!(choice, ColorChoice::Auto); |
80 | 0 | Self::new(raw, choice) |
81 | 0 | } Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::StderrLock>>::auto Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::StdoutLock>>::auto Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::Stderr>>::auto Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::Stdout>>::auto |
82 | | |
83 | | /// Report the desired choice for the given stream |
84 | | #[cfg(feature = "auto")] |
85 | 0 | pub fn choice(raw: &S) -> ColorChoice { |
86 | 0 | choice(raw) |
87 | 0 | } Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::StderrLock>>::choice Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::StdoutLock>>::choice Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::Stderr>>::choice Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::Stdout>>::choice |
88 | | |
89 | | /// Force ANSI escape codes to be passed through as-is, no matter what the inner `Write` |
90 | | /// supports. |
91 | | #[inline] |
92 | 0 | pub fn always_ansi(raw: S) -> Self { |
93 | | #[cfg(feature = "auto")] |
94 | | { |
95 | 0 | if raw.is_terminal() { |
96 | 0 | let _ = anstyle_query::windows::enable_ansi_colors(); |
97 | 0 | } |
98 | | } |
99 | 0 | Self::always_ansi_(raw) |
100 | 0 | } Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::StderrLock>>::always_ansi Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::StdoutLock>>::always_ansi Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::Stderr>>::always_ansi Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::Stdout>>::always_ansi |
101 | | |
102 | | #[inline] |
103 | 0 | fn always_ansi_(raw: S) -> Self { |
104 | 0 | let inner = StreamInner::PassThrough(raw); |
105 | 0 | AutoStream { inner } |
106 | 0 | } Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::StderrLock>>::always_ansi_ Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::StdoutLock>>::always_ansi_ Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::Stderr>>::always_ansi_ Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::Stdout>>::always_ansi_ |
107 | | |
108 | | /// Force color, no matter what the inner `Write` supports. |
109 | | #[inline] |
110 | 0 | pub fn always(raw: S) -> Self { |
111 | 0 | if cfg!(windows) { |
112 | | #[cfg(feature = "auto")] |
113 | 0 | let use_wincon = raw.is_terminal() |
114 | 0 | && !anstyle_query::windows::enable_ansi_colors().unwrap_or(true) |
115 | 0 | && !anstyle_query::term_supports_ansi_color(); |
116 | | #[cfg(not(feature = "auto"))] |
117 | | let use_wincon = true; |
118 | 0 | if use_wincon { |
119 | 0 | Self::wincon(raw).unwrap_or_else(|raw| Self::always_ansi_(raw)) |
120 | | } else { |
121 | 0 | Self::always_ansi_(raw) |
122 | | } |
123 | | } else { |
124 | 0 | Self::always_ansi(raw) |
125 | | } |
126 | 0 | } Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::StderrLock>>::always Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::StdoutLock>>::always Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::Stderr>>::always Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::Stdout>>::always |
127 | | |
128 | | /// Only pass printable data to the inner `Write`. |
129 | | #[inline] |
130 | 0 | pub fn never(raw: S) -> Self { |
131 | 0 | let inner = StreamInner::Strip(StripStream::new(raw)); |
132 | 0 | AutoStream { inner } |
133 | 0 | } Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::StderrLock>>::never Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::StdoutLock>>::never Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::Stderr>>::never Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::Stdout>>::never |
134 | | |
135 | | #[inline] |
136 | 0 | fn wincon(raw: S) -> Result<Self, S> { |
137 | | #[cfg(all(windows, feature = "wincon"))] |
138 | | { |
139 | | Ok(Self { |
140 | | inner: StreamInner::Wincon(WinconStream::new(raw)), |
141 | | }) |
142 | | } |
143 | | #[cfg(not(all(windows, feature = "wincon")))] |
144 | | { |
145 | 0 | Err(raw) |
146 | | } |
147 | 0 | } |
148 | | |
149 | | /// Get the wrapped [`RawStream`] |
150 | | #[inline] |
151 | 0 | pub fn into_inner(self) -> S { |
152 | 0 | match self.inner { |
153 | 0 | StreamInner::PassThrough(w) => w, |
154 | 0 | StreamInner::Strip(w) => w.into_inner(), |
155 | | #[cfg(all(windows, feature = "wincon"))] |
156 | | StreamInner::Wincon(w) => w.into_inner(), |
157 | | } |
158 | 0 | } |
159 | | |
160 | | /// Get the wrapped [`RawStream`] |
161 | | #[inline] |
162 | 0 | pub fn as_inner(&self) -> &S { |
163 | 0 | match &self.inner { |
164 | 0 | StreamInner::PassThrough(w) => w, |
165 | 0 | StreamInner::Strip(w) => w.as_inner(), |
166 | | #[cfg(all(windows, feature = "wincon"))] |
167 | | StreamInner::Wincon(w) => w.as_inner(), |
168 | | } |
169 | 0 | } |
170 | | |
171 | | /// Returns `true` if the descriptor/handle refers to a terminal/tty. |
172 | | #[inline] |
173 | 0 | pub fn is_terminal(&self) -> bool { |
174 | 0 | match &self.inner { |
175 | 0 | StreamInner::PassThrough(w) => w.is_terminal(), |
176 | 0 | StreamInner::Strip(w) => w.is_terminal(), |
177 | | #[cfg(all(windows, feature = "wincon"))] |
178 | | StreamInner::Wincon(_) => true, // its only ever a terminal |
179 | | } |
180 | 0 | } |
181 | | |
182 | | /// Prefer [`AutoStream::choice`] |
183 | | /// |
184 | | /// This doesn't report what is requested but what is currently active. |
185 | | #[inline] |
186 | | #[cfg(feature = "auto")] |
187 | 0 | pub fn current_choice(&self) -> ColorChoice { |
188 | 0 | match &self.inner { |
189 | 0 | StreamInner::PassThrough(_) => ColorChoice::AlwaysAnsi, |
190 | 0 | StreamInner::Strip(_) => ColorChoice::Never, |
191 | | #[cfg(all(windows, feature = "wincon"))] |
192 | | StreamInner::Wincon(_) => ColorChoice::Always, |
193 | | } |
194 | 0 | } |
195 | | } |
196 | | |
197 | | #[cfg(feature = "auto")] |
198 | 0 | fn choice(raw: &dyn RawStream) -> ColorChoice { |
199 | 0 | let choice = ColorChoice::global(); |
200 | 0 | match choice { |
201 | | ColorChoice::Auto => { |
202 | 0 | let clicolor = anstyle_query::clicolor(); |
203 | 0 | let clicolor_enabled = clicolor.unwrap_or(false); |
204 | 0 | let clicolor_disabled = !clicolor.unwrap_or(true); |
205 | 0 | if anstyle_query::no_color() { |
206 | 0 | ColorChoice::Never |
207 | 0 | } else if anstyle_query::clicolor_force() { |
208 | 0 | ColorChoice::Always |
209 | 0 | } else if clicolor_disabled { |
210 | 0 | ColorChoice::Never |
211 | 0 | } else if raw.is_terminal() |
212 | 0 | && (anstyle_query::term_supports_color() |
213 | 0 | || clicolor_enabled |
214 | 0 | || anstyle_query::is_ci()) |
215 | | { |
216 | 0 | ColorChoice::Always |
217 | | } else { |
218 | 0 | ColorChoice::Never |
219 | | } |
220 | | } |
221 | 0 | ColorChoice::AlwaysAnsi | ColorChoice::Always | ColorChoice::Never => choice, |
222 | | } |
223 | 0 | } |
224 | | |
225 | | impl AutoStream<std::io::Stdout> { |
226 | | /// Get exclusive access to the `AutoStream` |
227 | | /// |
228 | | /// Why? |
229 | | /// - Faster performance when writing in a loop |
230 | | /// - Avoid other threads interleaving output with the current thread |
231 | | #[inline] |
232 | 0 | pub fn lock(self) -> AutoStream<std::io::StdoutLock<'static>> { |
233 | 0 | let inner = match self.inner { |
234 | 0 | StreamInner::PassThrough(w) => StreamInner::PassThrough(w.lock()), |
235 | 0 | StreamInner::Strip(w) => StreamInner::Strip(w.lock()), |
236 | | #[cfg(all(windows, feature = "wincon"))] |
237 | | StreamInner::Wincon(w) => StreamInner::Wincon(w.lock()), |
238 | | }; |
239 | 0 | AutoStream { inner } |
240 | 0 | } |
241 | | } |
242 | | |
243 | | impl AutoStream<std::io::Stderr> { |
244 | | /// Get exclusive access to the `AutoStream` |
245 | | /// |
246 | | /// Why? |
247 | | /// - Faster performance when writing in a loop |
248 | | /// - Avoid other threads interleaving output with the current thread |
249 | | #[inline] |
250 | 0 | pub fn lock(self) -> AutoStream<std::io::StderrLock<'static>> { |
251 | 0 | let inner = match self.inner { |
252 | 0 | StreamInner::PassThrough(w) => StreamInner::PassThrough(w.lock()), |
253 | 0 | StreamInner::Strip(w) => StreamInner::Strip(w.lock()), |
254 | | #[cfg(all(windows, feature = "wincon"))] |
255 | | StreamInner::Wincon(w) => StreamInner::Wincon(w.lock()), |
256 | | }; |
257 | 0 | AutoStream { inner } |
258 | 0 | } |
259 | | } |
260 | | |
261 | | impl<S> std::io::Write for AutoStream<S> |
262 | | where |
263 | | S: RawStream + AsLockedWrite, |
264 | | { |
265 | | // Must forward all calls to ensure locking happens appropriately |
266 | | #[inline] |
267 | 0 | fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> { |
268 | 0 | match &mut self.inner { |
269 | 0 | StreamInner::PassThrough(w) => w.as_locked_write().write(buf), |
270 | 0 | StreamInner::Strip(w) => w.write(buf), |
271 | | #[cfg(all(windows, feature = "wincon"))] |
272 | | StreamInner::Wincon(w) => w.write(buf), |
273 | | } |
274 | 0 | } Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::StderrLock> as std::io::Write>::write Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::StdoutLock> as std::io::Write>::write Unexecuted instantiation: <anstream::auto::AutoStream<_> as std::io::Write>::write |
275 | | #[inline] |
276 | 0 | fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize> { |
277 | 0 | match &mut self.inner { |
278 | 0 | StreamInner::PassThrough(w) => w.as_locked_write().write_vectored(bufs), |
279 | 0 | StreamInner::Strip(w) => w.write_vectored(bufs), |
280 | | #[cfg(all(windows, feature = "wincon"))] |
281 | | StreamInner::Wincon(w) => w.write_vectored(bufs), |
282 | | } |
283 | 0 | } Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::StderrLock> as std::io::Write>::write_vectored Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::StdoutLock> as std::io::Write>::write_vectored Unexecuted instantiation: <anstream::auto::AutoStream<_> as std::io::Write>::write_vectored |
284 | | // is_write_vectored: nightly only |
285 | | #[inline] |
286 | 0 | fn flush(&mut self) -> std::io::Result<()> { |
287 | 0 | match &mut self.inner { |
288 | 0 | StreamInner::PassThrough(w) => w.as_locked_write().flush(), |
289 | 0 | StreamInner::Strip(w) => w.flush(), |
290 | | #[cfg(all(windows, feature = "wincon"))] |
291 | | StreamInner::Wincon(w) => w.flush(), |
292 | | } |
293 | 0 | } Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::StderrLock> as std::io::Write>::flush Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::StdoutLock> as std::io::Write>::flush Unexecuted instantiation: <anstream::auto::AutoStream<_> as std::io::Write>::flush |
294 | | #[inline] |
295 | 0 | fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> { |
296 | 0 | match &mut self.inner { |
297 | 0 | StreamInner::PassThrough(w) => w.as_locked_write().write_all(buf), |
298 | 0 | StreamInner::Strip(w) => w.write_all(buf), |
299 | | #[cfg(all(windows, feature = "wincon"))] |
300 | | StreamInner::Wincon(w) => w.write_all(buf), |
301 | | } |
302 | 0 | } Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::StderrLock> as std::io::Write>::write_all Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::StdoutLock> as std::io::Write>::write_all Unexecuted instantiation: <anstream::auto::AutoStream<_> as std::io::Write>::write_all |
303 | | // write_all_vectored: nightly only |
304 | | #[inline] |
305 | 0 | fn write_fmt(&mut self, args: std::fmt::Arguments<'_>) -> std::io::Result<()> { |
306 | 0 | match &mut self.inner { |
307 | 0 | StreamInner::PassThrough(w) => w.as_locked_write().write_fmt(args), |
308 | 0 | StreamInner::Strip(w) => w.write_fmt(args), |
309 | | #[cfg(all(windows, feature = "wincon"))] |
310 | | StreamInner::Wincon(w) => w.write_fmt(args), |
311 | | } |
312 | 0 | } Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::StderrLock> as std::io::Write>::write_fmt Unexecuted instantiation: <anstream::auto::AutoStream<std::io::stdio::StdoutLock> as std::io::Write>::write_fmt Unexecuted instantiation: <anstream::auto::AutoStream<_> as std::io::Write>::write_fmt |
313 | | } |