termcolor/
writers.rs

1use crate::{Color, ColorChoice, ColorSpec, HyperlinkSpec, WriteColor};
2use std::io::{self, Write};
3use std::sync::atomic::{AtomicBool, Ordering};
4
5#[cfg(windows)]
6use winapi_util::console as wincon;
7
8/// `std::io` implements `Stdout` and `Stderr` (and their `Lock` variants) as
9/// separate types, which makes it difficult to abstract over them. We use
10/// some simple internal enum types to work around this.
11enum StandardStreamType {
12    Stdout,
13    Stderr,
14    StdoutBuffered,
15    StderrBuffered,
16}
17
18#[derive(Debug)]
19enum IoStandardStream {
20    Stdout(io::Stdout),
21    Stderr(io::Stderr),
22    StdoutBuffered(io::BufWriter<io::Stdout>),
23    StderrBuffered(io::BufWriter<io::Stderr>),
24}
25
26impl IoStandardStream {
27    fn new(sty: StandardStreamType) -> IoStandardStream {
28        match sty {
29            StandardStreamType::Stdout => {
30                IoStandardStream::Stdout(io::stdout())
31            }
32            StandardStreamType::Stderr => {
33                IoStandardStream::Stderr(io::stderr())
34            }
35            StandardStreamType::StdoutBuffered => {
36                let wtr = io::BufWriter::new(io::stdout());
37                IoStandardStream::StdoutBuffered(wtr)
38            }
39            StandardStreamType::StderrBuffered => {
40                let wtr = io::BufWriter::new(io::stderr());
41                IoStandardStream::StderrBuffered(wtr)
42            }
43        }
44    }
45
46    fn lock(&self) -> IoStandardStreamLock<'_> {
47        match *self {
48            IoStandardStream::Stdout(ref s) => {
49                IoStandardStreamLock::StdoutLock(s.lock())
50            }
51            IoStandardStream::Stderr(ref s) => {
52                IoStandardStreamLock::StderrLock(s.lock())
53            }
54            IoStandardStream::StdoutBuffered(_)
55            | IoStandardStream::StderrBuffered(_) => {
56                // We don't permit this case to ever occur in the public API,
57                // so it's OK to panic.
58                panic!("cannot lock a buffered standard stream")
59            }
60        }
61    }
62}
63
64impl io::Write for IoStandardStream {
65    #[inline(always)]
66    fn write(&mut self, b: &[u8]) -> io::Result<usize> {
67        match *self {
68            IoStandardStream::Stdout(ref mut s) => s.write(b),
69            IoStandardStream::Stderr(ref mut s) => s.write(b),
70            IoStandardStream::StdoutBuffered(ref mut s) => s.write(b),
71            IoStandardStream::StderrBuffered(ref mut s) => s.write(b),
72        }
73    }
74
75    #[inline(always)]
76    fn flush(&mut self) -> io::Result<()> {
77        match *self {
78            IoStandardStream::Stdout(ref mut s) => s.flush(),
79            IoStandardStream::Stderr(ref mut s) => s.flush(),
80            IoStandardStream::StdoutBuffered(ref mut s) => s.flush(),
81            IoStandardStream::StderrBuffered(ref mut s) => s.flush(),
82        }
83    }
84}
85
86// Same rigmarole for the locked variants of the standard streams.
87
88#[derive(Debug)]
89enum IoStandardStreamLock<'a> {
90    StdoutLock(io::StdoutLock<'a>),
91    StderrLock(io::StderrLock<'a>),
92}
93
94impl<'a> io::Write for IoStandardStreamLock<'a> {
95    #[inline(always)]
96    fn write(&mut self, b: &[u8]) -> io::Result<usize> {
97        match *self {
98            IoStandardStreamLock::StdoutLock(ref mut s) => s.write(b),
99            IoStandardStreamLock::StderrLock(ref mut s) => s.write(b),
100        }
101    }
102
103    #[inline(always)]
104    fn flush(&mut self) -> io::Result<()> {
105        match *self {
106            IoStandardStreamLock::StdoutLock(ref mut s) => s.flush(),
107            IoStandardStreamLock::StderrLock(ref mut s) => s.flush(),
108        }
109    }
110}
111
112/// A standard stream for writing to stdout or stderr.
113///
114/// This satisfies both `io::Write` and `WriteColor`, and buffers writes
115/// until either `flush` is called or the buffer is full.
116#[derive(Debug)]
117pub struct StandardStream {
118    wtr: LossyStandardStream<WriterInner<IoStandardStream>>,
119}
120
121/// `StandardStreamLock` is a locked reference to a `StandardStream`.
122///
123/// This implements the `io::Write` and `WriteColor` traits, and is constructed
124/// via the `Write::lock` method.
125///
126/// The lifetime `'a` refers to the lifetime of the corresponding
127/// `StandardStream`.
128#[derive(Debug)]
129pub struct StandardStreamLock<'a> {
130    wtr: LossyStandardStream<WriterInnerLock<IoStandardStreamLock<'a>>>,
131}
132
133/// Like `StandardStream`, but does buffered writing.
134#[derive(Debug)]
135pub struct BufferedStandardStream {
136    wtr: LossyStandardStream<WriterInner<IoStandardStream>>,
137}
138
139/// WriterInner is a (limited) generic representation of a writer.
140#[derive(Debug)]
141enum WriterInner<W> {
142    NoColor(NoColor<W>),
143    Ansi(Ansi<W>),
144}
145
146/// WriterInnerLock is a (limited) generic representation of a writer.
147#[derive(Debug)]
148enum WriterInnerLock<W> {
149    NoColor(NoColor<W>),
150    Ansi(Ansi<W>),
151}
152
153impl StandardStream {
154    /// Create a new `StandardStream` with the given color preferences that
155    /// writes to standard output.
156    ///
157    /// If coloring is desired, ANSI escape sequences are used.
158    pub fn stdout(choice: ColorChoice) -> StandardStream {
159        let wtr = WriterInner::create(StandardStreamType::Stdout, choice);
160        StandardStream { wtr: LossyStandardStream::new(wtr) }
161    }
162
163    /// Create a new `StandardStream` with the given color preferences that
164    /// writes to standard error.
165    ///
166    /// If coloring is desired, ANSI escape sequences are used.
167    pub fn stderr(choice: ColorChoice) -> StandardStream {
168        let wtr = WriterInner::create(StandardStreamType::Stderr, choice);
169        StandardStream { wtr: LossyStandardStream::new(wtr) }
170    }
171
172    /// Lock the underlying writer.
173    ///
174    /// The lock guard returned also satisfies `io::Write` and
175    /// `WriteColor`.
176    ///
177    /// This method is **not reentrant**. It may panic if `lock` is called
178    /// while a `StandardStreamLock` is still alive.
179    pub fn lock(&self) -> StandardStreamLock<'_> {
180        StandardStreamLock::from_stream(self)
181    }
182}
183
184impl<'a> StandardStreamLock<'a> {
185    fn from_stream(stream: &StandardStream) -> StandardStreamLock<'_> {
186        let locked = match *stream.wtr.get_ref() {
187            WriterInner::NoColor(ref w) => {
188                WriterInnerLock::NoColor(NoColor(w.0.lock()))
189            }
190            WriterInner::Ansi(ref w) => {
191                WriterInnerLock::Ansi(Ansi(w.0.lock()))
192            }
193        };
194        StandardStreamLock { wtr: stream.wtr.wrap(locked) }
195    }
196}
197
198impl BufferedStandardStream {
199    /// Create a new `BufferedStandardStream` with the given color preferences
200    /// that writes to standard output via a buffered writer.
201    ///
202    /// If coloring is desired, ANSI escape sequences are used.
203    pub fn stdout(choice: ColorChoice) -> BufferedStandardStream {
204        let wtr =
205            WriterInner::create(StandardStreamType::StdoutBuffered, choice);
206        BufferedStandardStream { wtr: LossyStandardStream::new(wtr) }
207    }
208
209    /// Create a new `BufferedStandardStream` with the given color preferences
210    /// that writes to standard error via a buffered writer.
211    ///
212    /// If coloring is desired, ANSI escape sequences are used.
213    pub fn stderr(choice: ColorChoice) -> BufferedStandardStream {
214        let wtr =
215            WriterInner::create(StandardStreamType::StderrBuffered, choice);
216        BufferedStandardStream { wtr: LossyStandardStream::new(wtr) }
217    }
218}
219
220impl WriterInner<IoStandardStream> {
221    /// Create a new inner writer for a standard stream with the given color
222    /// preferences.
223    #[cfg(not(windows))]
224    fn create(
225        sty: StandardStreamType,
226        choice: ColorChoice,
227    ) -> WriterInner<IoStandardStream> {
228        if choice.should_attempt_color() {
229            WriterInner::Ansi(Ansi(IoStandardStream::new(sty)))
230        } else {
231            WriterInner::NoColor(NoColor(IoStandardStream::new(sty)))
232        }
233    }
234
235    #[cfg(windows)]
236    fn create(
237        sty: StandardStreamType,
238        choice: ColorChoice,
239    ) -> WriterInner<IoStandardStream> {
240        let enabled_virtual = if choice.should_attempt_color() {
241            let con_res = match sty {
242                StandardStreamType::Stdout
243                | StandardStreamType::StdoutBuffered => {
244                    wincon::Console::stdout()
245                }
246                StandardStreamType::Stderr
247                | StandardStreamType::StderrBuffered => {
248                    wincon::Console::stderr()
249                }
250            };
251            if let Ok(mut con) = con_res {
252                con.set_virtual_terminal_processing(true).is_ok()
253            } else {
254                false
255            }
256        } else {
257            false
258        };
259        if choice.should_attempt_color()
260            && (enabled_virtual || choice.should_force_ansi())
261        {
262            WriterInner::Ansi(Ansi(IoStandardStream::new(sty)))
263        } else {
264            WriterInner::NoColor(NoColor(IoStandardStream::new(sty)))
265        }
266    }
267}
268
269impl io::Write for StandardStream {
270    #[inline]
271    fn write(&mut self, b: &[u8]) -> io::Result<usize> {
272        self.wtr.write(b)
273    }
274
275    #[inline]
276    fn flush(&mut self) -> io::Result<()> {
277        self.wtr.flush()
278    }
279}
280
281impl WriteColor for StandardStream {
282    #[inline]
283    fn supports_color(&self) -> bool {
284        self.wtr.supports_color()
285    }
286
287    #[inline]
288    fn supports_hyperlinks(&self) -> bool {
289        self.wtr.supports_hyperlinks()
290    }
291
292    #[inline]
293    fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
294        self.wtr.set_color(spec)
295    }
296
297    #[inline]
298    fn set_hyperlink(&mut self, link: &HyperlinkSpec) -> io::Result<()> {
299        self.wtr.set_hyperlink(link)
300    }
301
302    #[inline]
303    fn reset(&mut self) -> io::Result<()> {
304        self.wtr.reset()
305    }
306}
307
308impl<'a> io::Write for StandardStreamLock<'a> {
309    #[inline]
310    fn write(&mut self, b: &[u8]) -> io::Result<usize> {
311        self.wtr.write(b)
312    }
313
314    #[inline]
315    fn flush(&mut self) -> io::Result<()> {
316        self.wtr.flush()
317    }
318}
319
320impl<'a> WriteColor for StandardStreamLock<'a> {
321    #[inline]
322    fn supports_color(&self) -> bool {
323        self.wtr.supports_color()
324    }
325
326    #[inline]
327    fn supports_hyperlinks(&self) -> bool {
328        self.wtr.supports_hyperlinks()
329    }
330
331    #[inline]
332    fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
333        self.wtr.set_color(spec)
334    }
335
336    #[inline]
337    fn set_hyperlink(&mut self, link: &HyperlinkSpec) -> io::Result<()> {
338        self.wtr.set_hyperlink(link)
339    }
340
341    #[inline]
342    fn reset(&mut self) -> io::Result<()> {
343        self.wtr.reset()
344    }
345}
346
347impl io::Write for BufferedStandardStream {
348    #[inline]
349    fn write(&mut self, b: &[u8]) -> io::Result<usize> {
350        self.wtr.write(b)
351    }
352
353    #[inline]
354    fn flush(&mut self) -> io::Result<()> {
355        self.wtr.flush()
356    }
357}
358
359impl WriteColor for BufferedStandardStream {
360    #[inline]
361    fn supports_color(&self) -> bool {
362        self.wtr.supports_color()
363    }
364
365    #[inline]
366    fn supports_hyperlinks(&self) -> bool {
367        self.wtr.supports_hyperlinks()
368    }
369
370    #[inline]
371    fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
372        self.wtr.set_color(spec)
373    }
374
375    #[inline]
376    fn set_hyperlink(&mut self, link: &HyperlinkSpec) -> io::Result<()> {
377        self.wtr.set_hyperlink(link)
378    }
379
380    #[inline]
381    fn reset(&mut self) -> io::Result<()> {
382        self.wtr.reset()
383    }
384}
385
386impl<W: io::Write> io::Write for WriterInner<W> {
387    #[inline(always)]
388    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
389        match *self {
390            WriterInner::NoColor(ref mut wtr) => wtr.write(buf),
391            WriterInner::Ansi(ref mut wtr) => wtr.write(buf),
392        }
393    }
394
395    #[inline(always)]
396    fn flush(&mut self) -> io::Result<()> {
397        match *self {
398            WriterInner::NoColor(ref mut wtr) => wtr.flush(),
399            WriterInner::Ansi(ref mut wtr) => wtr.flush(),
400        }
401    }
402}
403
404impl<W: io::Write> WriteColor for WriterInner<W> {
405    fn supports_color(&self) -> bool {
406        match *self {
407            WriterInner::NoColor(_) => false,
408            WriterInner::Ansi(_) => true,
409        }
410    }
411
412    fn supports_hyperlinks(&self) -> bool {
413        match *self {
414            WriterInner::NoColor(_) => false,
415            WriterInner::Ansi(_) => true,
416        }
417    }
418
419    fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
420        match *self {
421            WriterInner::NoColor(ref mut wtr) => wtr.set_color(spec),
422            WriterInner::Ansi(ref mut wtr) => wtr.set_color(spec),
423        }
424    }
425
426    fn set_hyperlink(&mut self, link: &HyperlinkSpec) -> io::Result<()> {
427        match *self {
428            WriterInner::NoColor(ref mut wtr) => wtr.set_hyperlink(link),
429            WriterInner::Ansi(ref mut wtr) => wtr.set_hyperlink(link),
430        }
431    }
432
433    fn reset(&mut self) -> io::Result<()> {
434        match *self {
435            WriterInner::NoColor(ref mut wtr) => wtr.reset(),
436            WriterInner::Ansi(ref mut wtr) => wtr.reset(),
437        }
438    }
439}
440
441impl<W: io::Write> io::Write for WriterInnerLock<W> {
442    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
443        match *self {
444            WriterInnerLock::NoColor(ref mut wtr) => wtr.write(buf),
445            WriterInnerLock::Ansi(ref mut wtr) => wtr.write(buf),
446        }
447    }
448
449    fn flush(&mut self) -> io::Result<()> {
450        match *self {
451            WriterInnerLock::NoColor(ref mut wtr) => wtr.flush(),
452            WriterInnerLock::Ansi(ref mut wtr) => wtr.flush(),
453        }
454    }
455}
456
457impl<W: io::Write> WriteColor for WriterInnerLock<W> {
458    fn supports_color(&self) -> bool {
459        match *self {
460            WriterInnerLock::NoColor(_) => false,
461            WriterInnerLock::Ansi(_) => true,
462        }
463    }
464
465    fn supports_hyperlinks(&self) -> bool {
466        match *self {
467            WriterInnerLock::NoColor(_) => false,
468            WriterInnerLock::Ansi(_) => true,
469        }
470    }
471
472    fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
473        match *self {
474            WriterInnerLock::NoColor(ref mut wtr) => wtr.set_color(spec),
475            WriterInnerLock::Ansi(ref mut wtr) => wtr.set_color(spec),
476        }
477    }
478
479    fn set_hyperlink(&mut self, link: &HyperlinkSpec) -> io::Result<()> {
480        match *self {
481            WriterInnerLock::NoColor(ref mut wtr) => wtr.set_hyperlink(link),
482            WriterInnerLock::Ansi(ref mut wtr) => wtr.set_hyperlink(link),
483        }
484    }
485
486    fn reset(&mut self) -> io::Result<()> {
487        match *self {
488            WriterInnerLock::NoColor(ref mut wtr) => wtr.reset(),
489            WriterInnerLock::Ansi(ref mut wtr) => wtr.reset(),
490        }
491    }
492}
493
494/// Writes colored buffers to stdout or stderr.
495///
496/// Writable buffers can be obtained by calling `buffer` on a `BufferWriter`.
497///
498/// This writer works with terminals that support ANSI escape sequences.
499///
500/// It is intended for a `BufferWriter` to be used from multiple threads
501/// simultaneously, but note that buffer printing is serialized.
502#[derive(Debug)]
503pub struct BufferWriter {
504    stream: LossyStandardStream<IoStandardStream>,
505    printed: AtomicBool,
506    separator: Option<Vec<u8>>,
507    use_color: bool,
508}
509
510impl BufferWriter {
511    /// Create a new `BufferWriter` that writes to a standard stream with the
512    /// given color preferences.
513    #[cfg(not(windows))]
514    fn create(sty: StandardStreamType, choice: ColorChoice) -> BufferWriter {
515        let use_color = choice.should_attempt_color();
516        BufferWriter {
517            stream: LossyStandardStream::new(IoStandardStream::new(sty)),
518            printed: AtomicBool::new(false),
519            separator: None,
520            use_color,
521        }
522    }
523
524    #[cfg(windows)]
525    fn create(sty: StandardStreamType, choice: ColorChoice) -> BufferWriter {
526        let enabled_virtual = if choice.should_attempt_color() {
527            let con_res = match sty {
528                StandardStreamType::Stdout
529                | StandardStreamType::StdoutBuffered => {
530                    wincon::Console::stdout()
531                }
532                StandardStreamType::Stderr
533                | StandardStreamType::StderrBuffered => {
534                    wincon::Console::stderr()
535                }
536            };
537            if let Ok(mut con) = con_res {
538                con.set_virtual_terminal_processing(true).is_ok()
539            } else {
540                false
541            }
542        } else {
543            false
544        };
545        let use_color = choice.should_attempt_color()
546            && (enabled_virtual || choice.should_force_ansi());
547        let is_console = match sty {
548            StandardStreamType::Stdout
549            | StandardStreamType::StdoutBuffered => {
550                wincon::Console::stdout().is_ok()
551            }
552            StandardStreamType::Stderr
553            | StandardStreamType::StderrBuffered => {
554                wincon::Console::stderr().is_ok()
555            }
556        };
557        let mut stream = LossyStandardStream::new(IoStandardStream::new(sty));
558        stream.is_console = is_console;
559        BufferWriter {
560            stream,
561            printed: AtomicBool::new(false),
562            separator: None,
563            use_color,
564        }
565    }
566
567    /// Create a new `BufferWriter` that writes to stdout with the given
568    /// color preferences.
569    pub fn stdout(choice: ColorChoice) -> BufferWriter {
570        BufferWriter::create(StandardStreamType::Stdout, choice)
571    }
572
573    /// Create a new `BufferWriter` that writes to stderr with the given
574    /// color preferences.
575    pub fn stderr(choice: ColorChoice) -> BufferWriter {
576        BufferWriter::create(StandardStreamType::Stderr, choice)
577    }
578
579    /// If set, the separator given is printed between buffers. By default, no
580    /// separator is printed.
581    ///
582    /// The default value is `None`.
583    pub fn separator(&mut self, sep: Option<Vec<u8>>) {
584        self.separator = sep;
585    }
586
587    /// Creates a new `Buffer` with the current color preferences.
588    ///
589    /// A `Buffer` satisfies both `io::Write` and `WriteColor`. A `Buffer` can
590    /// be printed using the `print` method.
591    pub fn buffer(&self) -> Buffer {
592        if self.use_color { Buffer::ansi() } else { Buffer::no_color() }
593    }
594
595    /// Prints the contents of the given buffer.
596    ///
597    /// It is safe to call this from multiple threads simultaneously. In
598    /// particular, all buffers are written atomically. No interleaving will
599    /// occur.
600    pub fn print(&self, buf: &Buffer) -> io::Result<()> {
601        if buf.is_empty() {
602            return Ok(());
603        }
604        let mut stream = self.stream.wrap(self.stream.get_ref().lock());
605        if let Some(ref sep) = self.separator
606            && self.printed.load(Ordering::Relaxed)
607        {
608            stream.write_all(sep)?;
609            stream.write_all(b"\n")?;
610        }
611        match buf.0 {
612            BufferInner::NoColor(ref b) => stream.write_all(&b.0)?,
613            BufferInner::Ansi(ref b) => stream.write_all(&b.0)?,
614        }
615        self.printed.store(true, Ordering::Relaxed);
616        Ok(())
617    }
618}
619
620/// Write colored text to memory.
621///
622/// `Buffer` is a platform independent abstraction for printing colored text to
623/// an in memory buffer. When the buffer is printed using a `BufferWriter`, the
624/// color information will be applied to the output device (a tty on Unix and
625/// Windows with virtual terminal support).
626///
627/// A `Buffer` is typically created by calling the `BufferWriter.buffer`
628/// method, which will take color preferences and the environment into
629/// account. However, buffers can also be manually created using `no_color`
630/// or `ansi`.
631#[derive(Clone, Debug)]
632pub struct Buffer(BufferInner);
633
634/// BufferInner is an enumeration of different buffer types.
635#[derive(Clone, Debug)]
636enum BufferInner {
637    /// No coloring information should be applied. This ignores all coloring
638    /// directives.
639    NoColor(NoColor<Vec<u8>>),
640    /// Apply coloring using ANSI escape sequences embedded into the buffer.
641    Ansi(Ansi<Vec<u8>>),
642}
643
644impl Buffer {
645    /// Create a new buffer with the given color settings.
646    #[cfg(not(windows))]
647    #[allow(dead_code)]
648    fn new(choice: ColorChoice) -> Buffer {
649        if choice.should_attempt_color() {
650            Buffer::ansi()
651        } else {
652            Buffer::no_color()
653        }
654    }
655
656    /// Create a new buffer with the given color settings.
657    #[cfg(windows)]
658    #[allow(dead_code)]
659    fn new(choice: ColorChoice) -> Buffer {
660        if choice.should_attempt_color() && choice.should_force_ansi() {
661            Buffer::ansi()
662        } else {
663            Buffer::no_color()
664        }
665    }
666
667    /// Create a buffer that drops all color information.
668    pub fn no_color() -> Buffer {
669        Buffer(BufferInner::NoColor(NoColor(vec![])))
670    }
671
672    /// Create a buffer that uses ANSI escape sequences.
673    pub fn ansi() -> Buffer {
674        Buffer(BufferInner::Ansi(Ansi(vec![])))
675    }
676
677    /// Returns true if and only if this buffer is empty.
678    pub fn is_empty(&self) -> bool {
679        self.len() == 0
680    }
681
682    /// Returns the length of this buffer in bytes.
683    pub fn len(&self) -> usize {
684        match self.0 {
685            BufferInner::NoColor(ref b) => b.0.len(),
686            BufferInner::Ansi(ref b) => b.0.len(),
687        }
688    }
689
690    /// Clears this buffer.
691    pub fn clear(&mut self) {
692        match self.0 {
693            BufferInner::NoColor(ref mut b) => b.0.clear(),
694            BufferInner::Ansi(ref mut b) => b.0.clear(),
695        }
696    }
697
698    /// Consume this buffer and return the underlying raw data.
699    pub fn into_inner(self) -> Vec<u8> {
700        match self.0 {
701            BufferInner::NoColor(b) => b.0,
702            BufferInner::Ansi(b) => b.0,
703        }
704    }
705
706    /// Return the underlying data of the buffer.
707    pub fn as_slice(&self) -> &[u8] {
708        match self.0 {
709            BufferInner::NoColor(ref b) => &b.0,
710            BufferInner::Ansi(ref b) => &b.0,
711        }
712    }
713
714    /// Return the underlying data of the buffer as a mutable slice.
715    pub fn as_mut_slice(&mut self) -> &mut [u8] {
716        match self.0 {
717            BufferInner::NoColor(ref mut b) => &mut b.0,
718            BufferInner::Ansi(ref mut b) => &mut b.0,
719        }
720    }
721}
722
723impl io::Write for Buffer {
724    #[inline]
725    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
726        match self.0 {
727            BufferInner::NoColor(ref mut w) => w.write(buf),
728            BufferInner::Ansi(ref mut w) => w.write(buf),
729        }
730    }
731
732    #[inline]
733    fn flush(&mut self) -> io::Result<()> {
734        match self.0 {
735            BufferInner::NoColor(ref mut w) => w.flush(),
736            BufferInner::Ansi(ref mut w) => w.flush(),
737        }
738    }
739}
740
741impl WriteColor for Buffer {
742    #[inline]
743    fn supports_color(&self) -> bool {
744        match self.0 {
745            BufferInner::NoColor(_) => false,
746            BufferInner::Ansi(_) => true,
747        }
748    }
749
750    #[inline]
751    fn supports_hyperlinks(&self) -> bool {
752        match self.0 {
753            BufferInner::NoColor(_) => false,
754            BufferInner::Ansi(_) => true,
755        }
756    }
757
758    #[inline]
759    fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
760        match self.0 {
761            BufferInner::NoColor(ref mut w) => w.set_color(spec),
762            BufferInner::Ansi(ref mut w) => w.set_color(spec),
763        }
764    }
765
766    #[inline]
767    fn set_hyperlink(&mut self, link: &HyperlinkSpec) -> io::Result<()> {
768        match self.0 {
769            BufferInner::NoColor(ref mut w) => w.set_hyperlink(link),
770            BufferInner::Ansi(ref mut w) => w.set_hyperlink(link),
771        }
772    }
773
774    #[inline]
775    fn reset(&mut self) -> io::Result<()> {
776        match self.0 {
777            BufferInner::NoColor(ref mut w) => w.reset(),
778            BufferInner::Ansi(ref mut w) => w.reset(),
779        }
780    }
781}
782
783/// Satisfies `WriteColor` but ignores all color options.
784#[derive(Clone, Debug)]
785pub struct NoColor<W>(pub W);
786
787impl<W: Write> NoColor<W> {
788    /// Create a new writer that satisfies `WriteColor` but drops all color
789    /// information.
790    pub fn new(wtr: W) -> NoColor<W> {
791        NoColor(wtr)
792    }
793
794    /// Consume this `NoColor` value and return the inner writer.
795    pub fn into_inner(self) -> W {
796        self.0
797    }
798
799    /// Return a reference to the inner writer.
800    pub fn get_ref(&self) -> &W {
801        &self.0
802    }
803
804    /// Return a mutable reference to the inner writer.
805    pub fn get_mut(&mut self) -> &mut W {
806        &mut self.0
807    }
808}
809
810impl<W: io::Write> io::Write for NoColor<W> {
811    #[inline]
812    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
813        self.0.write(buf)
814    }
815
816    #[inline]
817    fn flush(&mut self) -> io::Result<()> {
818        self.0.flush()
819    }
820}
821
822impl<W: io::Write> WriteColor for NoColor<W> {
823    #[inline]
824    fn supports_color(&self) -> bool {
825        false
826    }
827
828    #[inline]
829    fn supports_hyperlinks(&self) -> bool {
830        false
831    }
832
833    #[inline]
834    fn set_color(&mut self, _: &ColorSpec) -> io::Result<()> {
835        Ok(())
836    }
837
838    #[inline]
839    fn set_hyperlink(&mut self, _: &HyperlinkSpec) -> io::Result<()> {
840        Ok(())
841    }
842
843    #[inline]
844    fn reset(&mut self) -> io::Result<()> {
845        Ok(())
846    }
847}
848
849/// Satisfies `WriteColor` using standard ANSI escape sequences.
850#[derive(Clone, Debug)]
851pub struct Ansi<W>(pub W);
852
853impl<W: Write> Ansi<W> {
854    /// Create a new writer that satisfies `WriteColor` using standard ANSI
855    /// escape sequences.
856    pub fn new(wtr: W) -> Ansi<W> {
857        Ansi(wtr)
858    }
859
860    /// Consume this `Ansi` value and return the inner writer.
861    pub fn into_inner(self) -> W {
862        self.0
863    }
864
865    /// Return a reference to the inner writer.
866    pub fn get_ref(&self) -> &W {
867        &self.0
868    }
869
870    /// Return a mutable reference to the inner writer.
871    pub fn get_mut(&mut self) -> &mut W {
872        &mut self.0
873    }
874}
875
876impl<W: io::Write> io::Write for Ansi<W> {
877    #[inline]
878    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
879        self.0.write(buf)
880    }
881
882    // Adding this method here is not required because it has a default impl,
883    // but it seems to provide a perf improvement in some cases when using
884    // a `BufWriter` with lots of writes.
885    //
886    // See https://github.com/BurntSushi/termcolor/pull/56 for more details
887    // and a minimized example.
888    #[inline]
889    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
890        self.0.write_all(buf)
891    }
892
893    #[inline]
894    fn flush(&mut self) -> io::Result<()> {
895        self.0.flush()
896    }
897}
898
899impl<W: io::Write> WriteColor for Ansi<W> {
900    #[inline]
901    fn supports_color(&self) -> bool {
902        true
903    }
904
905    #[inline]
906    fn supports_hyperlinks(&self) -> bool {
907        true
908    }
909
910    #[inline]
911    fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
912        if spec.reset() {
913            self.reset()?;
914        }
915        if spec.bold() {
916            self.write_str("\x1B[1m")?;
917        }
918        if spec.dimmed() {
919            self.write_str("\x1B[2m")?;
920        }
921        if spec.italic() {
922            self.write_str("\x1B[3m")?;
923        }
924        if spec.underline() {
925            self.write_str("\x1B[4m")?;
926        }
927        if spec.strikethrough() {
928            self.write_str("\x1B[9m")?;
929        }
930        if let Some(c) = spec.fg() {
931            self.write_color(true, c, spec.intense())?;
932        }
933        if let Some(c) = spec.bg() {
934            self.write_color(false, c, spec.intense())?;
935        }
936        Ok(())
937    }
938
939    #[inline]
940    fn set_hyperlink(&mut self, link: &HyperlinkSpec) -> io::Result<()> {
941        self.write_str("\x1B]8;;")?;
942        if let Some(uri) = link.uri() {
943            self.write_all(uri)?;
944        }
945        self.write_str("\x1B\\")
946    }
947
948    #[inline]
949    fn reset(&mut self) -> io::Result<()> {
950        self.write_str("\x1B[0m")
951    }
952}
953
954impl<W: io::Write> Ansi<W> {
955    fn write_str(&mut self, s: &str) -> io::Result<()> {
956        self.write_all(s.as_bytes())
957    }
958
959    fn write_color(
960        &mut self,
961        fg: bool,
962        c: &Color,
963        intense: bool,
964    ) -> io::Result<()> {
965        macro_rules! write_intense {
966            ($clr:expr) => {
967                if fg {
968                    self.write_str(concat!("\x1B[38;5;", $clr, "m"))
969                } else {
970                    self.write_str(concat!("\x1B[48;5;", $clr, "m"))
971                }
972            };
973        }
974        macro_rules! write_normal {
975            ($clr:expr) => {
976                if fg {
977                    self.write_str(concat!("\x1B[3", $clr, "m"))
978                } else {
979                    self.write_str(concat!("\x1B[4", $clr, "m"))
980                }
981            };
982        }
983        macro_rules! write_var_ansi_code {
984            ($pre:expr, $($code:expr),+) => {{
985                // The loop generates at worst a literal of the form
986                // '255,255,255m' which is 12-bytes.
987                // The largest `pre` expression we currently use is 7 bytes.
988                // This gives us the maximum of 19-bytes for our work buffer.
989                let pre_len = $pre.len();
990                assert!(pre_len <= 7);
991                let mut fmt = [0u8; 19];
992                fmt[..pre_len].copy_from_slice($pre);
993                let mut i = pre_len - 1;
994                $(
995                    let c1: u8 = ($code / 100) % 10;
996                    let c2: u8 = ($code / 10) % 10;
997                    let c3: u8 = $code % 10;
998                    let mut printed = false;
999
1000                    if c1 != 0 {
1001                        printed = true;
1002                        i += 1;
1003                        fmt[i] = b'0' + c1;
1004                    }
1005                    if c2 != 0 || printed {
1006                        i += 1;
1007                        fmt[i] = b'0' + c2;
1008                    }
1009                    // If we received a zero value we must still print a value.
1010                    i += 1;
1011                    fmt[i] = b'0' + c3;
1012                    i += 1;
1013                    fmt[i] = b';';
1014                )+
1015
1016                fmt[i] = b'm';
1017                self.write_all(&fmt[0..i+1])
1018            }}
1019        }
1020        macro_rules! write_custom {
1021            ($ansi256:expr) => {
1022                if fg {
1023                    write_var_ansi_code!(b"\x1B[38;5;", $ansi256)
1024                } else {
1025                    write_var_ansi_code!(b"\x1B[48;5;", $ansi256)
1026                }
1027            };
1028
1029            ($r:expr, $g:expr, $b:expr) => {{
1030                if fg {
1031                    write_var_ansi_code!(b"\x1B[38;2;", $r, $g, $b)
1032                } else {
1033                    write_var_ansi_code!(b"\x1B[48;2;", $r, $g, $b)
1034                }
1035            }};
1036        }
1037        if intense {
1038            match *c {
1039                Color::Black => write_intense!("8"),
1040                Color::Blue => write_intense!("12"),
1041                Color::Green => write_intense!("10"),
1042                Color::Red => write_intense!("9"),
1043                Color::Cyan => write_intense!("14"),
1044                Color::Magenta => write_intense!("13"),
1045                Color::Yellow => write_intense!("11"),
1046                Color::White => write_intense!("15"),
1047                Color::Ansi256(c) => write_custom!(c),
1048                Color::Rgb(r, g, b) => write_custom!(r, g, b),
1049            }
1050        } else {
1051            match *c {
1052                Color::Black => write_normal!("0"),
1053                Color::Blue => write_normal!("4"),
1054                Color::Green => write_normal!("2"),
1055                Color::Red => write_normal!("1"),
1056                Color::Cyan => write_normal!("6"),
1057                Color::Magenta => write_normal!("5"),
1058                Color::Yellow => write_normal!("3"),
1059                Color::White => write_normal!("7"),
1060                Color::Ansi256(c) => write_custom!(c),
1061                Color::Rgb(r, g, b) => write_custom!(r, g, b),
1062            }
1063        }
1064    }
1065}
1066
1067impl WriteColor for io::Sink {
1068    fn supports_color(&self) -> bool {
1069        false
1070    }
1071
1072    fn supports_hyperlinks(&self) -> bool {
1073        false
1074    }
1075
1076    fn set_color(&mut self, _: &ColorSpec) -> io::Result<()> {
1077        Ok(())
1078    }
1079
1080    fn set_hyperlink(&mut self, _: &HyperlinkSpec) -> io::Result<()> {
1081        Ok(())
1082    }
1083
1084    fn reset(&mut self) -> io::Result<()> {
1085        Ok(())
1086    }
1087}
1088
1089#[derive(Debug)]
1090struct LossyStandardStream<W> {
1091    wtr: W,
1092    #[cfg(windows)]
1093    is_console: bool,
1094}
1095
1096impl<W: io::Write> LossyStandardStream<W> {
1097    #[cfg(not(windows))]
1098    fn new(wtr: W) -> LossyStandardStream<W> {
1099        LossyStandardStream { wtr }
1100    }
1101
1102    #[cfg(windows)]
1103    fn new(wtr: W) -> LossyStandardStream<W> {
1104        LossyStandardStream { wtr, is_console: false }
1105    }
1106
1107    #[cfg(not(windows))]
1108    fn wrap<Q: io::Write>(&self, wtr: Q) -> LossyStandardStream<Q> {
1109        LossyStandardStream::new(wtr)
1110    }
1111
1112    #[cfg(windows)]
1113    fn wrap<Q: io::Write>(&self, wtr: Q) -> LossyStandardStream<Q> {
1114        LossyStandardStream { wtr, is_console: self.is_console }
1115    }
1116
1117    fn get_ref(&self) -> &W {
1118        &self.wtr
1119    }
1120}
1121
1122impl<W: WriteColor> WriteColor for LossyStandardStream<W> {
1123    fn supports_color(&self) -> bool {
1124        self.wtr.supports_color()
1125    }
1126    fn supports_hyperlinks(&self) -> bool {
1127        self.wtr.supports_hyperlinks()
1128    }
1129    fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
1130        self.wtr.set_color(spec)
1131    }
1132    fn set_hyperlink(&mut self, link: &HyperlinkSpec) -> io::Result<()> {
1133        self.wtr.set_hyperlink(link)
1134    }
1135    fn reset(&mut self) -> io::Result<()> {
1136        self.wtr.reset()
1137    }
1138}
1139
1140impl<W: io::Write> io::Write for LossyStandardStream<W> {
1141    #[cfg(not(windows))]
1142    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1143        self.wtr.write(buf)
1144    }
1145
1146    #[cfg(windows)]
1147    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1148        if self.is_console {
1149            write_lossy_utf8(&mut self.wtr, buf)
1150        } else {
1151            self.wtr.write(buf)
1152        }
1153    }
1154
1155    fn flush(&mut self) -> io::Result<()> {
1156        self.wtr.flush()
1157    }
1158}
1159
1160#[cfg(windows)]
1161fn write_lossy_utf8<W: io::Write>(mut w: W, buf: &[u8]) -> io::Result<usize> {
1162    match ::std::str::from_utf8(buf) {
1163        Ok(s) => w.write(s.as_bytes()),
1164        Err(ref e) if e.valid_up_to() == 0 => {
1165            w.write(b"\xEF\xBF\xBD")?;
1166            Ok(1)
1167        }
1168        Err(e) => w.write(&buf[..e.valid_up_to()]),
1169    }
1170}
1171
1172impl WriteColor for Vec<u8> {
1173    fn supports_color(&self) -> bool {
1174        false // Vec<u8> doesn't support color output
1175    }
1176
1177    fn supports_hyperlinks(&self) -> bool {
1178        false // Vec<u8> doesn't support hyperlinks
1179    }
1180
1181    fn set_color(&mut self, _spec: &ColorSpec) -> io::Result<()> {
1182        Ok(()) // No-op for Vec<u8>
1183    }
1184
1185    fn set_hyperlink(&mut self, _link: &HyperlinkSpec) -> io::Result<()> {
1186        Ok(()) // No-op for Vec<u8>
1187    }
1188
1189    fn reset(&mut self) -> io::Result<()> {
1190        Ok(()) // No-op for Vec<u8>
1191    }
1192
1193    fn is_synchronous(&self) -> bool {
1194        false // Vec<u8> is not synchronous
1195    }
1196}
1197
1198/// A wrapper for String that implements both Write and WriteColor.
1199///
1200/// This fixes naga 26.0.0 compatibility where it expects WriteColor on string-like writers.
1201/// The StringWriter collects written data into an internal String buffer without any
1202/// color formatting (colors are ignored).
1203#[derive(Debug, Default)]
1204pub struct StringWriter {
1205    /// The internal string buffer that collects written data.
1206    pub inner: String,
1207}
1208
1209impl StringWriter {
1210    /// Creates a new empty StringWriter.
1211    pub fn new() -> Self {
1212        Self { inner: String::new() }
1213    }
1214
1215    /// Creates a new StringWriter with the specified capacity.
1216    pub fn with_capacity(capacity: usize) -> Self {
1217        Self { inner: String::with_capacity(capacity) }
1218    }
1219
1220    /// Consumes the StringWriter and returns the internal String.
1221    pub fn into_string(self) -> String {
1222        self.inner
1223    }
1224
1225    /// Returns a string slice of the internal buffer.
1226    pub fn as_str(&self) -> &str {
1227        &self.inner
1228    }
1229}
1230
1231impl io::Write for StringWriter {
1232    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1233        match std::str::from_utf8(buf) {
1234            Ok(s) => {
1235                self.inner.push_str(s);
1236                Ok(buf.len())
1237            }
1238            Err(_) => Err(io::Error::new(
1239                io::ErrorKind::InvalidData,
1240                "Invalid UTF-8",
1241            )),
1242        }
1243    }
1244
1245    fn flush(&mut self) -> io::Result<()> {
1246        Ok(())
1247    }
1248}
1249
1250impl WriteColor for StringWriter {
1251    fn supports_color(&self) -> bool {
1252        false // StringWriter doesn't support color output
1253    }
1254
1255    fn supports_hyperlinks(&self) -> bool {
1256        false // StringWriter doesn't support hyperlinks  
1257    }
1258
1259    fn set_color(&mut self, _spec: &ColorSpec) -> io::Result<()> {
1260        Ok(()) // No-op for StringWriter
1261    }
1262
1263    fn set_hyperlink(&mut self, _link: &HyperlinkSpec) -> io::Result<()> {
1264        Ok(()) // No-op for StringWriter
1265    }
1266
1267    fn reset(&mut self) -> io::Result<()> {
1268        Ok(()) // No-op for StringWriter
1269    }
1270
1271    fn is_synchronous(&self) -> bool {
1272        false // StringWriter is not synchronous
1273    }
1274}
1275
1276/// A String wrapper that implements both `io::Write` and `WriteColor`.
1277///
1278/// This type provides backward compatibility with libraries like naga that expect
1279/// string-like writers to implement `WriteColor`. It's a zero-cost wrapper around
1280/// `String` that adds the necessary trait implementations.
1281#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1282#[repr(transparent)]
1283pub struct TermString(pub String);
1284
1285impl TermString {
1286    /// Creates a new empty `TermString`.
1287    pub fn new() -> Self {
1288        Self(String::new())
1289    }
1290
1291    /// Creates a new `TermString` with the specified capacity.
1292    pub fn with_capacity(capacity: usize) -> Self {
1293        Self(String::with_capacity(capacity))
1294    }
1295
1296    /// Consumes the `TermString` and returns the inner `String`.
1297    pub fn into_inner(self) -> String {
1298        self.0
1299    }
1300
1301    /// Returns a string slice of the `TermString` contents.
1302    pub fn as_str(&self) -> &str {
1303        &self.0
1304    }
1305
1306    /// Appends a string slice to the end of this `TermString`.
1307    pub fn push_str(&mut self, s: &str) {
1308        self.0.push_str(s)
1309    }
1310}
1311
1312impl From<String> for TermString {
1313    fn from(s: String) -> Self {
1314        Self(s)
1315    }
1316}
1317
1318impl From<TermString> for String {
1319    fn from(ts: TermString) -> Self {
1320        ts.0
1321    }
1322}
1323
1324impl AsRef<str> for TermString {
1325    fn as_ref(&self) -> &str {
1326        &self.0
1327    }
1328}
1329
1330impl std::fmt::Display for TermString {
1331    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1332        self.0.fmt(f)
1333    }
1334}
1335
1336// Now we can implement io::Write for our TermString!
1337impl io::Write for TermString {
1338    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1339        match std::str::from_utf8(buf) {
1340            Ok(s) => {
1341                self.0.push_str(s);
1342                Ok(buf.len())
1343            }
1344            Err(_) => Err(io::Error::new(
1345                io::ErrorKind::InvalidData,
1346                "Invalid UTF-8",
1347            )),
1348        }
1349    }
1350
1351    fn flush(&mut self) -> io::Result<()> {
1352        Ok(()) // String doesn't need flushing
1353    }
1354}
1355
1356// And WriteColor for TermString
1357impl WriteColor for TermString {
1358    fn supports_color(&self) -> bool {
1359        false // TermString doesn't support color output
1360    }
1361
1362    fn supports_hyperlinks(&self) -> bool {
1363        false // TermString doesn't support hyperlinks  
1364    }
1365
1366    fn set_color(&mut self, _spec: &ColorSpec) -> io::Result<()> {
1367        Ok(()) // No-op for TermString
1368    }
1369
1370    fn set_hyperlink(&mut self, _link: &HyperlinkSpec) -> io::Result<()> {
1371        Ok(()) // No-op for TermString
1372    }
1373
1374    fn reset(&mut self) -> io::Result<()> {
1375        Ok(()) // No-op for TermString
1376    }
1377
1378    fn is_synchronous(&self) -> bool {
1379        false // TermString is not synchronous
1380    }
1381}