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
8enum 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 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#[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#[derive(Debug)]
117pub struct StandardStream {
118 wtr: LossyStandardStream<WriterInner<IoStandardStream>>,
119}
120
121#[derive(Debug)]
129pub struct StandardStreamLock<'a> {
130 wtr: LossyStandardStream<WriterInnerLock<IoStandardStreamLock<'a>>>,
131}
132
133#[derive(Debug)]
135pub struct BufferedStandardStream {
136 wtr: LossyStandardStream<WriterInner<IoStandardStream>>,
137}
138
139#[derive(Debug)]
141enum WriterInner<W> {
142 NoColor(NoColor<W>),
143 Ansi(Ansi<W>),
144}
145
146#[derive(Debug)]
148enum WriterInnerLock<W> {
149 NoColor(NoColor<W>),
150 Ansi(Ansi<W>),
151}
152
153impl StandardStream {
154 pub fn stdout(choice: ColorChoice) -> StandardStream {
159 let wtr = WriterInner::create(StandardStreamType::Stdout, choice);
160 StandardStream { wtr: LossyStandardStream::new(wtr) }
161 }
162
163 pub fn stderr(choice: ColorChoice) -> StandardStream {
168 let wtr = WriterInner::create(StandardStreamType::Stderr, choice);
169 StandardStream { wtr: LossyStandardStream::new(wtr) }
170 }
171
172 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 pub fn stdout(choice: ColorChoice) -> BufferedStandardStream {
204 let wtr =
205 WriterInner::create(StandardStreamType::StdoutBuffered, choice);
206 BufferedStandardStream { wtr: LossyStandardStream::new(wtr) }
207 }
208
209 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 #[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#[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 #[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 pub fn stdout(choice: ColorChoice) -> BufferWriter {
570 BufferWriter::create(StandardStreamType::Stdout, choice)
571 }
572
573 pub fn stderr(choice: ColorChoice) -> BufferWriter {
576 BufferWriter::create(StandardStreamType::Stderr, choice)
577 }
578
579 pub fn separator(&mut self, sep: Option<Vec<u8>>) {
584 self.separator = sep;
585 }
586
587 pub fn buffer(&self) -> Buffer {
592 if self.use_color { Buffer::ansi() } else { Buffer::no_color() }
593 }
594
595 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#[derive(Clone, Debug)]
632pub struct Buffer(BufferInner);
633
634#[derive(Clone, Debug)]
636enum BufferInner {
637 NoColor(NoColor<Vec<u8>>),
640 Ansi(Ansi<Vec<u8>>),
642}
643
644impl Buffer {
645 #[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 #[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 pub fn no_color() -> Buffer {
669 Buffer(BufferInner::NoColor(NoColor(vec![])))
670 }
671
672 pub fn ansi() -> Buffer {
674 Buffer(BufferInner::Ansi(Ansi(vec![])))
675 }
676
677 pub fn is_empty(&self) -> bool {
679 self.len() == 0
680 }
681
682 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 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 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 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 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#[derive(Clone, Debug)]
785pub struct NoColor<W>(pub W);
786
787impl<W: Write> NoColor<W> {
788 pub fn new(wtr: W) -> NoColor<W> {
791 NoColor(wtr)
792 }
793
794 pub fn into_inner(self) -> W {
796 self.0
797 }
798
799 pub fn get_ref(&self) -> &W {
801 &self.0
802 }
803
804 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#[derive(Clone, Debug)]
851pub struct Ansi<W>(pub W);
852
853impl<W: Write> Ansi<W> {
854 pub fn new(wtr: W) -> Ansi<W> {
857 Ansi(wtr)
858 }
859
860 pub fn into_inner(self) -> W {
862 self.0
863 }
864
865 pub fn get_ref(&self) -> &W {
867 &self.0
868 }
869
870 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 #[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 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 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 }
1176
1177 fn supports_hyperlinks(&self) -> bool {
1178 false }
1180
1181 fn set_color(&mut self, _spec: &ColorSpec) -> io::Result<()> {
1182 Ok(()) }
1184
1185 fn set_hyperlink(&mut self, _link: &HyperlinkSpec) -> io::Result<()> {
1186 Ok(()) }
1188
1189 fn reset(&mut self) -> io::Result<()> {
1190 Ok(()) }
1192
1193 fn is_synchronous(&self) -> bool {
1194 false }
1196}
1197
1198#[derive(Debug, Default)]
1204pub struct StringWriter {
1205 pub inner: String,
1207}
1208
1209impl StringWriter {
1210 pub fn new() -> Self {
1212 Self { inner: String::new() }
1213 }
1214
1215 pub fn with_capacity(capacity: usize) -> Self {
1217 Self { inner: String::with_capacity(capacity) }
1218 }
1219
1220 pub fn into_string(self) -> String {
1222 self.inner
1223 }
1224
1225 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 }
1254
1255 fn supports_hyperlinks(&self) -> bool {
1256 false }
1258
1259 fn set_color(&mut self, _spec: &ColorSpec) -> io::Result<()> {
1260 Ok(()) }
1262
1263 fn set_hyperlink(&mut self, _link: &HyperlinkSpec) -> io::Result<()> {
1264 Ok(()) }
1266
1267 fn reset(&mut self) -> io::Result<()> {
1268 Ok(()) }
1270
1271 fn is_synchronous(&self) -> bool {
1272 false }
1274}
1275
1276#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1282#[repr(transparent)]
1283pub struct TermString(pub String);
1284
1285impl TermString {
1286 pub fn new() -> Self {
1288 Self(String::new())
1289 }
1290
1291 pub fn with_capacity(capacity: usize) -> Self {
1293 Self(String::with_capacity(capacity))
1294 }
1295
1296 pub fn into_inner(self) -> String {
1298 self.0
1299 }
1300
1301 pub fn as_str(&self) -> &str {
1303 &self.0
1304 }
1305
1306 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
1336impl 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(()) }
1354}
1355
1356impl WriteColor for TermString {
1358 fn supports_color(&self) -> bool {
1359 false }
1361
1362 fn supports_hyperlinks(&self) -> bool {
1363 false }
1365
1366 fn set_color(&mut self, _spec: &ColorSpec) -> io::Result<()> {
1367 Ok(()) }
1369
1370 fn set_hyperlink(&mut self, _link: &HyperlinkSpec) -> io::Result<()> {
1371 Ok(()) }
1373
1374 fn reset(&mut self) -> io::Result<()> {
1375 Ok(()) }
1377
1378 fn is_synchronous(&self) -> bool {
1379 false }
1381}