1#[cfg(feature = "auto")]
2use crate::ColorChoice;
3use crate::Lockable;
4use crate::RawStream;
5use crate::StripStream;
6#[cfg(all(windows, feature = "wincon"))]
7use crate::WinconStream;
8
9#[derive(Debug)]
11pub struct AutoStream<S: RawStream> {
12 inner: StreamInner<S>,
13}
14
15#[derive(Debug)]
16enum StreamInner<S: RawStream> {
17 PassThrough(S),
18 Strip(StripStream<S>),
19 #[cfg(all(windows, feature = "wincon"))]
20 Wincon(WinconStream<S>),
21}
22
23impl<S> AutoStream<S>
24where
25 S: RawStream,
26{
27 #[cfg(feature = "auto")]
29 #[inline]
30 pub fn new(raw: S, choice: ColorChoice) -> Self {
31 match choice {
32 ColorChoice::Auto => Self::auto(raw),
33 ColorChoice::AlwaysAnsi => Self::always_ansi(raw),
34 ColorChoice::Always => Self::always(raw),
35 ColorChoice::Never => Self::never(raw),
36 }
37 }
38
39 #[cfg(feature = "auto")]
41 #[inline]
42 pub fn auto(raw: S) -> Self {
43 let choice = Self::choice(&raw);
44 debug_assert_ne!(choice, ColorChoice::Auto);
45 Self::new(raw, choice)
46 }
47
48 #[cfg(feature = "auto")]
50 #[inline]
51 pub fn choice(raw: &S) -> ColorChoice {
52 let choice = concolor_override::get();
53 match choice {
54 ColorChoice::Auto => {
55 let clicolor = concolor_query::clicolor();
56 let clicolor_enabled = clicolor.unwrap_or(false);
57 let clicolor_disabled = !clicolor.unwrap_or(true);
58 if raw.is_terminal()
59 && !concolor_query::no_color()
60 && !clicolor_disabled
61 && (concolor_query::term_supports_color()
62 || clicolor_enabled
63 || concolor_query::is_ci())
64 || concolor_query::clicolor_force()
65 {
66 ColorChoice::Always
67 } else {
68 ColorChoice::Never
69 }
70 }
71 ColorChoice::AlwaysAnsi | ColorChoice::Always | ColorChoice::Never => choice,
72 }
73 }
74
75 #[inline]
78 pub fn always_ansi(raw: S) -> Self {
79 #[cfg(feature = "auto")]
80 {
81 if raw.is_terminal() {
82 let _ = concolor_query::windows::enable_ansi_colors();
83 }
84 }
85 Self::always_ansi_(raw)
86 }
87
88 #[inline]
89 fn always_ansi_(raw: S) -> Self {
90 let inner = StreamInner::PassThrough(raw);
91 AutoStream { inner }
92 }
93
94 #[inline]
96 pub fn always(raw: S) -> Self {
97 if cfg!(windows) {
98 #[cfg(feature = "auto")]
99 let use_wincon = raw.is_terminal()
100 && !concolor_query::windows::enable_ansi_colors().unwrap_or(true)
101 && !concolor_query::term_supports_ansi_color();
102 #[cfg(not(feature = "auto"))]
103 let use_wincon = true;
104 if use_wincon {
105 Self::wincon(raw).unwrap_or_else(|raw| Self::always_ansi_(raw))
106 } else {
107 Self::always_ansi_(raw)
108 }
109 } else {
110 Self::always_ansi(raw)
111 }
112 }
113
114 #[inline]
116 pub fn never(raw: S) -> Self {
117 let inner = StreamInner::Strip(StripStream::new(raw));
118 AutoStream { inner }
119 }
120
121 #[inline]
122 fn wincon(raw: S) -> Result<Self, S> {
123 #[cfg(all(windows, feature = "wincon"))]
124 {
125 let console = anstyle_wincon::Console::new(raw)?;
126 Ok(Self {
127 inner: StreamInner::Wincon(WinconStream::new(console)),
128 })
129 }
130 #[cfg(not(all(windows, feature = "wincon")))]
131 {
132 Err(raw)
133 }
134 }
135
136 #[inline]
138 pub fn into_inner(self) -> S {
139 match self.inner {
140 StreamInner::PassThrough(w) => w,
141 StreamInner::Strip(w) => w.into_inner(),
142 #[cfg(all(windows, feature = "wincon"))]
143 StreamInner::Wincon(w) => w.into_inner().into_inner(),
144 }
145 }
146
147 #[inline]
148 #[cfg(feature = "auto")]
149 pub fn is_terminal(&self) -> bool {
150 match &self.inner {
151 StreamInner::PassThrough(w) => w.is_terminal(),
152 StreamInner::Strip(w) => w.is_terminal(),
153 #[cfg(all(windows, feature = "wincon"))]
154 StreamInner::Wincon(w) => true,
155 }
156 }
157}
158
159#[cfg(feature = "auto")]
160impl<S> is_terminal::IsTerminal for AutoStream<S>
161where
162 S: RawStream,
163{
164 #[inline]
165 fn is_terminal(&self) -> bool {
166 self.is_terminal()
167 }
168}
169
170impl<S> AutoStream<S>
171where
172 S: Lockable + RawStream,
173 <S as Lockable>::Locked: RawStream,
174{
175 #[inline]
181 pub fn lock(self) -> <Self as Lockable>::Locked {
182 let inner = match self.inner {
183 StreamInner::PassThrough(w) => StreamInner::PassThrough(w.lock()),
184 StreamInner::Strip(w) => StreamInner::Strip(w.lock()),
185 #[cfg(all(windows, feature = "wincon"))]
186 StreamInner::Wincon(w) => StreamInner::Wincon(w.lock()),
187 };
188 AutoStream { inner }
189 }
190}
191
192impl<S> std::io::Write for AutoStream<S>
193where
194 S: RawStream,
195{
196 #[inline]
197 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
198 match &mut self.inner {
199 StreamInner::PassThrough(w) => w.write(buf),
200 StreamInner::Strip(w) => w.write(buf),
201 #[cfg(all(windows, feature = "wincon"))]
202 StreamInner::Wincon(w) => w.write(buf),
203 }
204 }
205
206 #[inline]
207 fn flush(&mut self) -> std::io::Result<()> {
208 match &mut self.inner {
209 StreamInner::PassThrough(w) => w.flush(),
210 StreamInner::Strip(w) => w.flush(),
211 #[cfg(all(windows, feature = "wincon"))]
212 StreamInner::Wincon(w) => w.flush(),
213 }
214 }
215
216 #[inline]
221 fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> {
222 match &mut self.inner {
223 StreamInner::PassThrough(w) => w.write_all(buf),
224 StreamInner::Strip(w) => w.write_all(buf),
225 #[cfg(all(windows, feature = "wincon"))]
226 StreamInner::Wincon(w) => w.write_all(buf),
227 }
228 }
229
230 }
232
233impl<S> Lockable for AutoStream<S>
234where
235 S: Lockable + RawStream,
236 <S as Lockable>::Locked: RawStream,
237{
238 type Locked = AutoStream<<S as Lockable>::Locked>;
239
240 #[inline]
241 fn lock(self) -> Self::Locked {
242 self.lock()
243 }
244}