1#![deny(missing_docs)]
73
74#[cfg(windows)]
75extern crate wincolor;
76
77use std::env;
78use std::error;
79use std::fmt;
80use std::io::{self, Write};
81use std::str::FromStr;
82#[cfg(windows)]
83use std::sync::{Mutex, MutexGuard};
84use std::sync::atomic::{AtomicBool, Ordering};
85
86pub trait WriteColor: io::Write {
88 fn supports_color(&self) -> bool;
90
91 fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()>;
99
100 fn reset(&mut self) -> io::Result<()>;
105
106 fn is_synchronous(&self) -> bool {
118 false
119 }
120}
121
122impl<'a, T: ?Sized + WriteColor> WriteColor for &'a mut T {
123 fn supports_color(&self) -> bool { (&**self).supports_color() }
124 fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
125 (&mut **self).set_color(spec)
126 }
127 fn reset(&mut self) -> io::Result<()> { (&mut **self).reset() }
128 fn is_synchronous(&self) -> bool { (&**self).is_synchronous() }
129}
130
131impl<T: ?Sized + WriteColor> WriteColor for Box<T> {
132 fn supports_color(&self) -> bool { (&**self).supports_color() }
133 fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
134 (&mut **self).set_color(spec)
135 }
136 fn reset(&mut self) -> io::Result<()> { (&mut **self).reset() }
137 fn is_synchronous(&self) -> bool { (&**self).is_synchronous() }
138}
139
140#[derive(Clone, Copy, Debug, Eq, PartialEq)]
142pub enum ColorChoice {
143 Always,
146 AlwaysAnsi,
149 Auto,
153 Never,
155}
156
157impl ColorChoice {
158 #[cfg(not(windows))]
160 fn should_attempt_color(&self) -> bool {
161 match *self {
162 ColorChoice::Always => true,
163 ColorChoice::AlwaysAnsi => true,
164 ColorChoice::Never => false,
165 ColorChoice::Auto => {
166 match env::var("TERM") {
167 Err(_) => false,
168 Ok(k) => k != "dumb",
169 }
170 }
171 }
172 }
173
174 #[cfg(windows)]
176 fn should_attempt_color(&self) -> bool {
177 match *self {
178 ColorChoice::Always => true,
179 ColorChoice::AlwaysAnsi => true,
180 ColorChoice::Never => false,
181 ColorChoice::Auto => {
182 match env::var("TERM") {
183 Err(_) => true,
184 Ok(k) => k != "dumb",
185 }
186 }
187 }
188 }
189
190 #[cfg(windows)]
195 fn should_ansi(&self) -> bool {
196 match *self {
197 ColorChoice::Always => false,
198 ColorChoice::AlwaysAnsi => true,
199 ColorChoice::Never => false,
200 ColorChoice::Auto => {
201 match env::var("TERM") {
202 Err(_) => false,
203 Ok(k) => k != "dumb" && k != "cygwin",
207 }
208 }
209 }
210 }
211}
212
213enum StandardStreamType {
218 Stdout,
219 Stderr,
220 StdoutBuffered,
221 StderrBuffered,
222}
223
224enum IoStandardStream {
225 Stdout(io::Stdout),
226 Stderr(io::Stderr),
227 StdoutBuffered(io::BufWriter<io::Stdout>),
228 StderrBuffered(io::BufWriter<io::Stderr>),
229}
230
231impl IoStandardStream {
232 fn new(sty: StandardStreamType) -> IoStandardStream {
233 match sty {
234 StandardStreamType::Stdout => {
235 IoStandardStream::Stdout(io::stdout())
236 }
237 StandardStreamType::Stderr => {
238 IoStandardStream::Stderr(io::stderr())
239 }
240 StandardStreamType::StdoutBuffered => {
241 let wtr = io::BufWriter::new(io::stdout());
242 IoStandardStream::StdoutBuffered(wtr)
243 }
244 StandardStreamType::StderrBuffered => {
245 let wtr = io::BufWriter::new(io::stderr());
246 IoStandardStream::StderrBuffered(wtr)
247 }
248 }
249 }
250
251 fn lock(&self) -> IoStandardStreamLock {
252 match *self {
253 IoStandardStream::Stdout(ref s) => {
254 IoStandardStreamLock::StdoutLock(s.lock())
255 }
256 IoStandardStream::Stderr(ref s) => {
257 IoStandardStreamLock::StderrLock(s.lock())
258 }
259 IoStandardStream::StdoutBuffered(_)
260 | IoStandardStream::StderrBuffered(_) => {
261 panic!("cannot lock a buffered standard stream")
264 }
265 }
266 }
267}
268
269impl io::Write for IoStandardStream {
270 #[inline(always)]
271 fn write(&mut self, b: &[u8]) -> io::Result<usize> {
272 match *self {
273 IoStandardStream::Stdout(ref mut s) => s.write(b),
274 IoStandardStream::Stderr(ref mut s) => s.write(b),
275 IoStandardStream::StdoutBuffered(ref mut s) => s.write(b),
276 IoStandardStream::StderrBuffered(ref mut s) => s.write(b),
277 }
278 }
279
280 #[inline(always)]
281 fn flush(&mut self) -> io::Result<()> {
282 match *self {
283 IoStandardStream::Stdout(ref mut s) => s.flush(),
284 IoStandardStream::Stderr(ref mut s) => s.flush(),
285 IoStandardStream::StdoutBuffered(ref mut s) => s.flush(),
286 IoStandardStream::StderrBuffered(ref mut s) => s.flush(),
287 }
288 }
289}
290
291enum IoStandardStreamLock<'a> {
294 StdoutLock(io::StdoutLock<'a>),
295 StderrLock(io::StderrLock<'a>),
296}
297
298impl<'a> io::Write for IoStandardStreamLock<'a> {
299 #[inline(always)]
300 fn write(&mut self, b: &[u8]) -> io::Result<usize> {
301 match *self {
302 IoStandardStreamLock::StdoutLock(ref mut s) => s.write(b),
303 IoStandardStreamLock::StderrLock(ref mut s) => s.write(b),
304 }
305 }
306
307 #[inline(always)]
308 fn flush(&mut self) -> io::Result<()> {
309 match *self {
310 IoStandardStreamLock::StdoutLock(ref mut s) => s.flush(),
311 IoStandardStreamLock::StderrLock(ref mut s) => s.flush(),
312 }
313 }
314}
315
316pub struct StandardStream {
319 wtr: LossyStandardStream<WriterInner<IoStandardStream>>,
320}
321
322pub struct StandardStreamLock<'a> {
330 wtr: LossyStandardStream<WriterInnerLock<'a, IoStandardStreamLock<'a>>>,
331}
332
333pub struct BufferedStandardStream {
335 wtr: LossyStandardStream<WriterInner<IoStandardStream>>,
336}
337
338enum WriterInner<W> {
341 NoColor(NoColor<W>),
342 Ansi(Ansi<W>),
343 #[cfg(windows)]
344 Windows { wtr: W, console: Mutex<wincolor::Console> },
345}
346
347enum WriterInnerLock<'a, W> {
350 NoColor(NoColor<W>),
351 Ansi(Ansi<W>),
352 #[allow(dead_code)]
357 Unreachable(::std::marker::PhantomData<&'a ()>),
358 #[cfg(windows)]
359 Windows { wtr: W, console: MutexGuard<'a, wincolor::Console> },
360}
361
362impl StandardStream {
363 pub fn stdout(choice: ColorChoice) -> StandardStream {
372 let wtr = WriterInner::create(StandardStreamType::Stdout, choice);
373 StandardStream { wtr: LossyStandardStream::new(wtr) }
374 }
375
376 pub fn stderr(choice: ColorChoice) -> StandardStream {
385 let wtr = WriterInner::create(StandardStreamType::Stderr, choice);
386 StandardStream { wtr: LossyStandardStream::new(wtr) }
387 }
388
389 pub fn lock(&self) -> StandardStreamLock {
397 StandardStreamLock::from_stream(self)
398 }
399}
400
401impl<'a> StandardStreamLock<'a> {
402 #[cfg(not(windows))]
403 fn from_stream(stream: &StandardStream) -> StandardStreamLock {
404 let locked = match *stream.wtr.get_ref() {
405 WriterInner::NoColor(ref w) => {
406 WriterInnerLock::NoColor(NoColor(w.0.lock()))
407 }
408 WriterInner::Ansi(ref w) => {
409 WriterInnerLock::Ansi(Ansi(w.0.lock()))
410 }
411 };
412 StandardStreamLock { wtr: stream.wtr.wrap(locked) }
413 }
414
415 #[cfg(windows)]
416 fn from_stream(stream: &StandardStream) -> StandardStreamLock {
417 let locked = match *stream.wtr.get_ref() {
418 WriterInner::NoColor(ref w) => {
419 WriterInnerLock::NoColor(NoColor(w.0.lock()))
420 }
421 WriterInner::Ansi(ref w) => {
422 WriterInnerLock::Ansi(Ansi(w.0.lock()))
423 }
424 #[cfg(windows)]
425 WriterInner::Windows { ref wtr, ref console } => {
426 WriterInnerLock::Windows {
427 wtr: wtr.lock(),
428 console: console.lock().unwrap(),
429 }
430 }
431 };
432 StandardStreamLock { wtr: stream.wtr.wrap(locked) }
433 }
434}
435
436impl BufferedStandardStream {
437 pub fn stdout(choice: ColorChoice) -> BufferedStandardStream {
446 let wtr = WriterInner::create(
447 StandardStreamType::StdoutBuffered,
448 choice,
449 );
450 BufferedStandardStream { wtr: LossyStandardStream::new(wtr) }
451 }
452
453 pub fn stderr(choice: ColorChoice) -> BufferedStandardStream {
462 let wtr = WriterInner::create(
463 StandardStreamType::StderrBuffered,
464 choice,
465 );
466 BufferedStandardStream { wtr: LossyStandardStream::new(wtr) }
467 }
468}
469
470impl WriterInner<IoStandardStream> {
471 #[cfg(not(windows))]
474 fn create(
475 sty: StandardStreamType,
476 choice: ColorChoice,
477 ) -> WriterInner<IoStandardStream> {
478 if choice.should_attempt_color() {
479 WriterInner::Ansi(Ansi(IoStandardStream::new(sty)))
480 } else {
481 WriterInner::NoColor(NoColor(IoStandardStream::new(sty)))
482 }
483 }
484
485 #[cfg(windows)]
491 fn create(
492 sty: StandardStreamType,
493 choice: ColorChoice,
494 ) -> WriterInner<IoStandardStream> {
495 let mut con = match sty {
496 StandardStreamType::Stdout => wincolor::Console::stdout(),
497 StandardStreamType::Stderr => wincolor::Console::stderr(),
498 StandardStreamType::StdoutBuffered => wincolor::Console::stdout(),
499 StandardStreamType::StderrBuffered => wincolor::Console::stderr(),
500 };
501 let is_console_virtual = con.as_mut().map(|con| {
502 con.set_virtual_terminal_processing(true).is_ok()
503 }).unwrap_or(false);
504 if choice.should_attempt_color() {
505 if choice.should_ansi() || is_console_virtual {
506 WriterInner::Ansi(Ansi(IoStandardStream::new(sty)))
507 } else if let Ok(console) = con {
508 WriterInner::Windows {
509 wtr: IoStandardStream::new(sty),
510 console: Mutex::new(console),
511 }
512 } else {
513 WriterInner::Ansi(Ansi(IoStandardStream::new(sty)))
514 }
515 } else {
516 WriterInner::NoColor(NoColor(IoStandardStream::new(sty)))
517 }
518 }
519}
520
521impl io::Write for StandardStream {
522 #[inline]
523 fn write(&mut self, b: &[u8]) -> io::Result<usize> { self.wtr.write(b) }
524
525 #[inline]
526 fn flush(&mut self) -> io::Result<()> { self.wtr.flush() }
527}
528
529impl WriteColor for StandardStream {
530 #[inline]
531 fn supports_color(&self) -> bool { self.wtr.supports_color() }
532
533 #[inline]
534 fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
535 self.wtr.set_color(spec)
536 }
537
538 #[inline]
539 fn reset(&mut self) -> io::Result<()> { self.wtr.reset() }
540
541 #[inline]
542 fn is_synchronous(&self) -> bool { self.wtr.is_synchronous() }
543}
544
545impl<'a> io::Write for StandardStreamLock<'a> {
546 #[inline]
547 fn write(&mut self, b: &[u8]) -> io::Result<usize> { self.wtr.write(b) }
548
549 #[inline]
550 fn flush(&mut self) -> io::Result<()> { self.wtr.flush() }
551}
552
553impl<'a> WriteColor for StandardStreamLock<'a> {
554 #[inline]
555 fn supports_color(&self) -> bool { self.wtr.supports_color() }
556
557 #[inline]
558 fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
559 self.wtr.set_color(spec)
560 }
561
562 #[inline]
563 fn reset(&mut self) -> io::Result<()> { self.wtr.reset() }
564
565 #[inline]
566 fn is_synchronous(&self) -> bool { self.wtr.is_synchronous() }
567}
568
569impl io::Write for BufferedStandardStream {
570 #[inline]
571 fn write(&mut self, b: &[u8]) -> io::Result<usize> { self.wtr.write(b) }
572
573 #[inline]
574 fn flush(&mut self) -> io::Result<()> { self.wtr.flush() }
575}
576
577impl WriteColor for BufferedStandardStream {
578 #[inline]
579 fn supports_color(&self) -> bool { self.wtr.supports_color() }
580
581 #[inline]
582 fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
583 if self.is_synchronous() {
584 self.wtr.flush()?;
585 }
586 self.wtr.set_color(spec)
587 }
588
589 #[inline]
590 fn reset(&mut self) -> io::Result<()> { self.wtr.reset() }
591
592 #[inline]
593 fn is_synchronous(&self) -> bool { self.wtr.is_synchronous() }
594}
595
596impl<W: io::Write> io::Write for WriterInner<W> {
597 #[inline(always)]
598 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
599 match *self {
600 WriterInner::NoColor(ref mut wtr) => wtr.write(buf),
601 WriterInner::Ansi(ref mut wtr) => wtr.write(buf),
602 #[cfg(windows)]
603 WriterInner::Windows { ref mut wtr, .. } => wtr.write(buf),
604 }
605 }
606
607 #[inline(always)]
608 fn flush(&mut self) -> io::Result<()> {
609 match *self {
610 WriterInner::NoColor(ref mut wtr) => wtr.flush(),
611 WriterInner::Ansi(ref mut wtr) => wtr.flush(),
612 #[cfg(windows)]
613 WriterInner::Windows { ref mut wtr, .. } => wtr.flush(),
614 }
615 }
616}
617
618impl<W: io::Write> WriteColor for WriterInner<W> {
619 fn supports_color(&self) -> bool {
620 match *self {
621 WriterInner::NoColor(_) => false,
622 WriterInner::Ansi(_) => true,
623 #[cfg(windows)]
624 WriterInner::Windows { .. } => true,
625 }
626 }
627
628 fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
629 match *self {
630 WriterInner::NoColor(ref mut wtr) => wtr.set_color(spec),
631 WriterInner::Ansi(ref mut wtr) => wtr.set_color(spec),
632 #[cfg(windows)]
633 WriterInner::Windows { ref mut wtr, ref console } => {
634 wtr.flush()?;
635 let mut console = console.lock().unwrap();
636 spec.write_console(&mut *console)
637 }
638 }
639 }
640
641 fn reset(&mut self) -> io::Result<()> {
642 match *self {
643 WriterInner::NoColor(ref mut wtr) => wtr.reset(),
644 WriterInner::Ansi(ref mut wtr) => wtr.reset(),
645 #[cfg(windows)]
646 WriterInner::Windows { ref mut wtr, ref mut console } => {
647 wtr.flush()?;
648 console.lock().unwrap().reset()?;
649 Ok(())
650 }
651 }
652 }
653
654 fn is_synchronous(&self) -> bool {
655 match *self {
656 WriterInner::NoColor(_) => false,
657 WriterInner::Ansi(_) => false,
658 #[cfg(windows)]
659 WriterInner::Windows {..} => true,
660 }
661 }
662}
663
664impl<'a, W: io::Write> io::Write for WriterInnerLock<'a, W> {
665 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
666 match *self {
667 WriterInnerLock::Unreachable(_) => unreachable!(),
668 WriterInnerLock::NoColor(ref mut wtr) => wtr.write(buf),
669 WriterInnerLock::Ansi(ref mut wtr) => wtr.write(buf),
670 #[cfg(windows)]
671 WriterInnerLock::Windows { ref mut wtr, .. } => wtr.write(buf),
672 }
673 }
674
675 fn flush(&mut self) -> io::Result<()> {
676 match *self {
677 WriterInnerLock::Unreachable(_) => unreachable!(),
678 WriterInnerLock::NoColor(ref mut wtr) => wtr.flush(),
679 WriterInnerLock::Ansi(ref mut wtr) => wtr.flush(),
680 #[cfg(windows)]
681 WriterInnerLock::Windows { ref mut wtr, .. } => wtr.flush(),
682 }
683 }
684}
685
686impl<'a, W: io::Write> WriteColor for WriterInnerLock<'a, W> {
687 fn supports_color(&self) -> bool {
688 match *self {
689 WriterInnerLock::Unreachable(_) => unreachable!(),
690 WriterInnerLock::NoColor(_) => false,
691 WriterInnerLock::Ansi(_) => true,
692 #[cfg(windows)]
693 WriterInnerLock::Windows { .. } => true,
694 }
695 }
696
697 fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
698 match *self {
699 WriterInnerLock::Unreachable(_) => unreachable!(),
700 WriterInnerLock::NoColor(ref mut wtr) => wtr.set_color(spec),
701 WriterInnerLock::Ansi(ref mut wtr) => wtr.set_color(spec),
702 #[cfg(windows)]
703 WriterInnerLock::Windows { ref mut wtr, ref mut console } => {
704 wtr.flush()?;
705 spec.write_console(console)
706 }
707 }
708 }
709
710 fn reset(&mut self) -> io::Result<()> {
711 match *self {
712 WriterInnerLock::Unreachable(_) => unreachable!(),
713 WriterInnerLock::NoColor(ref mut wtr) => wtr.reset(),
714 WriterInnerLock::Ansi(ref mut wtr) => wtr.reset(),
715 #[cfg(windows)]
716 WriterInnerLock::Windows { ref mut wtr, ref mut console } => {
717 wtr.flush()?;
718 console.reset()?;
719 Ok(())
720 }
721 }
722 }
723
724 fn is_synchronous(&self) -> bool {
725 match *self {
726 WriterInnerLock::Unreachable(_) => unreachable!(),
727 WriterInnerLock::NoColor(_) => false,
728 WriterInnerLock::Ansi(_) => false,
729 #[cfg(windows)]
730 WriterInnerLock::Windows {..} => true,
731 }
732 }
733}
734
735pub struct BufferWriter {
745 stream: LossyStandardStream<IoStandardStream>,
746 printed: AtomicBool,
747 separator: Option<Vec<u8>>,
748 color_choice: ColorChoice,
749 #[cfg(windows)]
750 console: Option<Mutex<wincolor::Console>>,
751}
752
753impl BufferWriter {
754 #[cfg(not(windows))]
760 fn create(sty: StandardStreamType, choice: ColorChoice) -> BufferWriter {
761 BufferWriter {
762 stream: LossyStandardStream::new(IoStandardStream::new(sty)),
763 printed: AtomicBool::new(false),
764 separator: None,
765 color_choice: choice,
766 }
767 }
768
769 #[cfg(windows)]
778 fn create(sty: StandardStreamType, choice: ColorChoice) -> BufferWriter {
779 let mut con = match sty {
780 StandardStreamType::Stdout => wincolor::Console::stdout(),
781 StandardStreamType::Stderr => wincolor::Console::stderr(),
782 StandardStreamType::StdoutBuffered => wincolor::Console::stdout(),
783 StandardStreamType::StderrBuffered => wincolor::Console::stderr(),
784 }.ok();
785 let is_console_virtual = con.as_mut().map(|con| {
786 con.set_virtual_terminal_processing(true).is_ok()
787 }).unwrap_or(false);
788 if is_console_virtual {
791 con = None;
792 }
793 let stream = LossyStandardStream::new(IoStandardStream::new(sty));
794 BufferWriter {
795 stream: stream,
796 printed: AtomicBool::new(false),
797 separator: None,
798 color_choice: choice,
799 console: con.map(Mutex::new),
800 }
801 }
802
803 pub fn stdout(choice: ColorChoice) -> BufferWriter {
812 BufferWriter::create(StandardStreamType::Stdout, choice)
813 }
814
815 pub fn stderr(choice: ColorChoice) -> BufferWriter {
824 BufferWriter::create(StandardStreamType::Stderr, choice)
825 }
826
827 pub fn separator(&mut self, sep: Option<Vec<u8>>) {
832 self.separator = sep;
833 }
834
835 #[cfg(not(windows))]
840 pub fn buffer(&self) -> Buffer {
841 Buffer::new(self.color_choice)
842 }
843
844 #[cfg(windows)]
849 pub fn buffer(&self) -> Buffer {
850 Buffer::new(self.color_choice, self.console.is_some())
851 }
852
853 pub fn print(&self, buf: &Buffer) -> io::Result<()> {
859 if buf.is_empty() {
860 return Ok(());
861 }
862 let mut stream = self.stream.wrap(self.stream.get_ref().lock());
863 if let Some(ref sep) = self.separator {
864 if self.printed.load(Ordering::SeqCst) {
865 stream.write_all(sep)?;
866 stream.write_all(b"\n")?;
867 }
868 }
869 match buf.0 {
870 BufferInner::NoColor(ref b) => stream.write_all(&b.0)?,
871 BufferInner::Ansi(ref b) => stream.write_all(&b.0)?,
872 #[cfg(windows)]
873 BufferInner::Windows(ref b) => {
874 let console_mutex = self.console.as_ref()
877 .expect("got Windows buffer but have no Console");
878 let mut console = console_mutex.lock().unwrap();
879 b.print(&mut *console, &mut stream)?;
880 }
881 }
882 self.printed.store(true, Ordering::SeqCst);
883 Ok(())
884 }
885}
886
887pub struct Buffer(BufferInner);
899
900enum BufferInner {
902 NoColor(NoColor<Vec<u8>>),
905 Ansi(Ansi<Vec<u8>>),
907 #[cfg(windows)]
911 Windows(WindowsBuffer),
912}
913
914impl Buffer {
915 #[cfg(not(windows))]
917 fn new(choice: ColorChoice) -> Buffer {
918 if choice.should_attempt_color() {
919 Buffer::ansi()
920 } else {
921 Buffer::no_color()
922 }
923 }
924
925 #[cfg(windows)]
933 fn new(choice: ColorChoice, console: bool) -> Buffer {
934 if choice.should_attempt_color() {
935 if !console || choice.should_ansi() {
936 Buffer::ansi()
937 } else {
938 Buffer::console()
939 }
940 } else {
941 Buffer::no_color()
942 }
943 }
944
945 pub fn no_color() -> Buffer {
947 Buffer(BufferInner::NoColor(NoColor(vec![])))
948 }
949
950 pub fn ansi() -> Buffer {
952 Buffer(BufferInner::Ansi(Ansi(vec![])))
953 }
954
955 #[cfg(windows)]
957 pub fn console() -> Buffer {
958 Buffer(BufferInner::Windows(WindowsBuffer::new()))
959 }
960
961 pub fn is_empty(&self) -> bool {
963 self.len() == 0
964 }
965
966 pub fn len(&self) -> usize {
968 match self.0 {
969 BufferInner::NoColor(ref b) => b.0.len(),
970 BufferInner::Ansi(ref b) => b.0.len(),
971 #[cfg(windows)]
972 BufferInner::Windows(ref b) => b.buf.len(),
973 }
974 }
975
976 pub fn clear(&mut self) {
978 match self.0 {
979 BufferInner::NoColor(ref mut b) => b.0.clear(),
980 BufferInner::Ansi(ref mut b) => b.0.clear(),
981 #[cfg(windows)]
982 BufferInner::Windows(ref mut b) => b.clear(),
983 }
984 }
985
986 pub fn into_inner(self) -> Vec<u8> {
991 match self.0 {
992 BufferInner::NoColor(b) => b.0,
993 BufferInner::Ansi(b) => b.0,
994 #[cfg(windows)]
995 BufferInner::Windows(b) => b.buf,
996 }
997 }
998
999 pub fn as_slice(&self) -> &[u8] {
1001 match self.0 {
1002 BufferInner::NoColor(ref b) => &b.0,
1003 BufferInner::Ansi(ref b) => &b.0,
1004 #[cfg(windows)]
1005 BufferInner::Windows(ref b) => &b.buf,
1006 }
1007 }
1008
1009 pub fn as_mut_slice(&mut self) -> &mut [u8] {
1011 match self.0 {
1012 BufferInner::NoColor(ref mut b) => &mut b.0,
1013 BufferInner::Ansi(ref mut b) => &mut b.0,
1014 #[cfg(windows)]
1015 BufferInner::Windows(ref mut b) => &mut b.buf,
1016 }
1017 }
1018}
1019
1020impl io::Write for Buffer {
1021 #[inline]
1022 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1023 match self.0 {
1024 BufferInner::NoColor(ref mut w) => w.write(buf),
1025 BufferInner::Ansi(ref mut w) => w.write(buf),
1026 #[cfg(windows)]
1027 BufferInner::Windows(ref mut w) => w.write(buf),
1028 }
1029 }
1030
1031 #[inline]
1032 fn flush(&mut self) -> io::Result<()> {
1033 match self.0 {
1034 BufferInner::NoColor(ref mut w) => w.flush(),
1035 BufferInner::Ansi(ref mut w) => w.flush(),
1036 #[cfg(windows)]
1037 BufferInner::Windows(ref mut w) => w.flush(),
1038 }
1039 }
1040}
1041
1042impl WriteColor for Buffer {
1043 #[inline]
1044 fn supports_color(&self) -> bool {
1045 match self.0 {
1046 BufferInner::NoColor(_) => false,
1047 BufferInner::Ansi(_) => true,
1048 #[cfg(windows)]
1049 BufferInner::Windows(_) => true,
1050 }
1051 }
1052
1053 #[inline]
1054 fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
1055 match self.0 {
1056 BufferInner::NoColor(ref mut w) => w.set_color(spec),
1057 BufferInner::Ansi(ref mut w) => w.set_color(spec),
1058 #[cfg(windows)]
1059 BufferInner::Windows(ref mut w) => w.set_color(spec),
1060 }
1061 }
1062
1063 #[inline]
1064 fn reset(&mut self) -> io::Result<()> {
1065 match self.0 {
1066 BufferInner::NoColor(ref mut w) => w.reset(),
1067 BufferInner::Ansi(ref mut w) => w.reset(),
1068 #[cfg(windows)]
1069 BufferInner::Windows(ref mut w) => w.reset(),
1070 }
1071 }
1072
1073 #[inline]
1074 fn is_synchronous(&self) -> bool {
1075 false
1076 }
1077}
1078
1079pub struct NoColor<W>(W);
1081
1082impl<W: Write> NoColor<W> {
1083 pub fn new(wtr: W) -> NoColor<W> { NoColor(wtr) }
1086
1087 pub fn into_inner(self) -> W { self.0 }
1089
1090 pub fn get_ref(&self) -> &W { &self.0 }
1092
1093 pub fn get_mut(&mut self) -> &mut W { &mut self.0 }
1095}
1096
1097impl<W: io::Write> io::Write for NoColor<W> {
1098 #[inline]
1099 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1100 self.0.write(buf)
1101 }
1102
1103 #[inline]
1104 fn flush(&mut self) -> io::Result<()> {
1105 self.0.flush()
1106 }
1107}
1108
1109impl<W: io::Write> WriteColor for NoColor<W> {
1110 #[inline]
1111 fn supports_color(&self) -> bool { false }
1112
1113 #[inline]
1114 fn set_color(&mut self, _: &ColorSpec) -> io::Result<()> { Ok(()) }
1115
1116 #[inline]
1117 fn reset(&mut self) -> io::Result<()> { Ok(()) }
1118
1119 #[inline]
1120 fn is_synchronous(&self) -> bool { false }
1121}
1122
1123pub struct Ansi<W>(W);
1125
1126impl<W: Write> Ansi<W> {
1127 pub fn new(wtr: W) -> Ansi<W> { Ansi(wtr) }
1130
1131 pub fn into_inner(self) -> W { self.0 }
1133
1134 pub fn get_ref(&self) -> &W { &self.0 }
1136
1137 pub fn get_mut(&mut self) -> &mut W { &mut self.0 }
1139}
1140
1141impl<W: io::Write> io::Write for Ansi<W> {
1142 #[inline]
1143 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1144 self.0.write(buf)
1145 }
1146
1147 #[inline]
1148 fn flush(&mut self) -> io::Result<()> {
1149 self.0.flush()
1150 }
1151}
1152
1153impl<W: io::Write> WriteColor for Ansi<W> {
1154 #[inline]
1155 fn supports_color(&self) -> bool { true }
1156
1157 #[inline]
1158 fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
1159 self.reset()?;
1160 if spec.bold {
1161 self.write_str("\x1B[1m")?;
1162 }
1163 if spec.underline {
1164 self.write_str("\x1B[4m")?;
1165 }
1166 if let Some(ref c) = spec.fg_color {
1167 self.write_color(true, c, spec.intense)?;
1168 }
1169 if let Some(ref c) = spec.bg_color {
1170 self.write_color(false, c, spec.intense)?;
1171 }
1172 Ok(())
1173 }
1174
1175 #[inline]
1176 fn reset(&mut self) -> io::Result<()> {
1177 self.write_str("\x1B[0m")
1178 }
1179
1180 #[inline]
1181 fn is_synchronous(&self) -> bool { false }
1182}
1183
1184impl<W: io::Write> Ansi<W> {
1185 fn write_str(&mut self, s: &str) -> io::Result<()> {
1186 self.write_all(s.as_bytes())
1187 }
1188
1189 fn write_color(
1190 &mut self,
1191 fg: bool,
1192 c: &Color,
1193 intense: bool,
1194 ) -> io::Result<()> {
1195 macro_rules! write_intense {
1196 ($clr:expr) => {
1197 if fg {
1198 self.write_str(concat!("\x1B[38;5;", $clr, "m"))
1199 } else {
1200 self.write_str(concat!("\x1B[48;5;", $clr, "m"))
1201 }
1202 }
1203 }
1204 macro_rules! write_normal {
1205 ($clr:expr) => {
1206 if fg {
1207 self.write_str(concat!("\x1B[3", $clr, "m"))
1208 } else {
1209 self.write_str(concat!("\x1B[4", $clr, "m"))
1210 }
1211 }
1212 }
1213 macro_rules! write_var_ansi_code {
1214 ($pre:expr, $($code:expr),+) => {{
1215 let pre_len = $pre.len();
1220 assert!(pre_len <= 7);
1221 let mut fmt = [0u8; 19];
1222 fmt[..pre_len].copy_from_slice($pre);
1223 let mut i = pre_len - 1;
1224 $(
1225 let c1: u8 = ($code / 100) % 10;
1226 let c2: u8 = ($code / 10) % 10;
1227 let c3: u8 = $code % 10;
1228 let mut printed = false;
1229
1230 if c1 != 0 {
1231 printed = true;
1232 i += 1;
1233 fmt[i] = b'0' + c1;
1234 }
1235 if c2 != 0 || printed {
1236 i += 1;
1237 fmt[i] = b'0' + c2;
1238 }
1239 i += 1;
1241 fmt[i] = b'0' + c3;
1242 i += 1;
1243 fmt[i] = b';';
1244 )+
1245
1246 fmt[i] = b'm';
1247 self.write_all(&fmt[0..i+1])
1248 }}
1249 }
1250 macro_rules! write_custom {
1251 ($ansi256:expr) => {
1252 if fg {
1253 write_var_ansi_code!(b"\x1B[38;5;", $ansi256)
1254 } else {
1255 write_var_ansi_code!(b"\x1B[48;5;", $ansi256)
1256 }
1257 };
1258
1259 ($r:expr, $g:expr, $b:expr) => {{
1260 if fg {
1261 write_var_ansi_code!(b"\x1B[38;2;", $r, $g, $b)
1262 } else {
1263 write_var_ansi_code!(b"\x1B[48;2;", $r, $g, $b)
1264 }
1265 }};
1266 }
1267 if intense {
1268 match *c {
1269 Color::Black => write_intense!("8"),
1270 Color::Blue => write_intense!("12"),
1271 Color::Green => write_intense!("10"),
1272 Color::Red => write_intense!("9"),
1273 Color::Cyan => write_intense!("14"),
1274 Color::Magenta => write_intense!("13"),
1275 Color::Yellow => write_intense!("11"),
1276 Color::White => write_intense!("15"),
1277 Color::Ansi256(c) => write_custom!(c),
1278 Color::Rgb(r, g, b) => write_custom!(r, g, b),
1279 Color::__Nonexhaustive => unreachable!(),
1280 }
1281 } else {
1282 match *c {
1283 Color::Black => write_normal!("0"),
1284 Color::Blue => write_normal!("4"),
1285 Color::Green => write_normal!("2"),
1286 Color::Red => write_normal!("1"),
1287 Color::Cyan => write_normal!("6"),
1288 Color::Magenta => write_normal!("5"),
1289 Color::Yellow => write_normal!("3"),
1290 Color::White => write_normal!("7"),
1291 Color::Ansi256(c) => write_custom!(c),
1292 Color::Rgb(r, g, b) => write_custom!(r, g, b),
1293 Color::__Nonexhaustive => unreachable!(),
1294 }
1295 }
1296 }
1297}
1298
1299#[cfg(windows)]
1315#[derive(Clone, Debug)]
1316struct WindowsBuffer {
1317 buf: Vec<u8>,
1319 colors: Vec<(usize, Option<ColorSpec>)>,
1325}
1326
1327#[cfg(windows)]
1328impl WindowsBuffer {
1329 fn new() -> WindowsBuffer {
1331 WindowsBuffer {
1332 buf: vec![],
1333 colors: vec![],
1334 }
1335 }
1336
1337 fn push(&mut self, spec: Option<ColorSpec>) {
1342 let pos = self.buf.len();
1343 self.colors.push((pos, spec));
1344 }
1345
1346 fn print(
1349 &self,
1350 console: &mut wincolor::Console,
1351 stream: &mut LossyStandardStream<IoStandardStreamLock>,
1352 ) -> io::Result<()> {
1353 let mut last = 0;
1354 for &(pos, ref spec) in &self.colors {
1355 stream.write_all(&self.buf[last..pos])?;
1356 stream.flush()?;
1357 last = pos;
1358 match *spec {
1359 None => console.reset()?,
1360 Some(ref spec) => spec.write_console(console)?,
1361 }
1362 }
1363 stream.write_all(&self.buf[last..])?;
1364 stream.flush()
1365 }
1366
1367 fn clear(&mut self) {
1369 self.buf.clear();
1370 self.colors.clear();
1371 }
1372}
1373
1374#[cfg(windows)]
1375impl io::Write for WindowsBuffer {
1376 #[inline]
1377 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1378 self.buf.extend_from_slice(buf);
1379 Ok(buf.len())
1380 }
1381
1382 #[inline]
1383 fn flush(&mut self) -> io::Result<()> {
1384 Ok(())
1385 }
1386}
1387
1388#[cfg(windows)]
1389impl WriteColor for WindowsBuffer {
1390 #[inline]
1391 fn supports_color(&self) -> bool { true }
1392
1393 #[inline]
1394 fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
1395 self.push(Some(spec.clone()));
1396 Ok(())
1397 }
1398
1399 #[inline]
1400 fn reset(&mut self) -> io::Result<()> {
1401 self.push(None);
1402 Ok(())
1403 }
1404
1405 #[inline]
1406 fn is_synchronous(&self) -> bool {
1407 false
1408 }
1409}
1410
1411#[derive(Clone, Debug, Default, Eq, PartialEq)]
1413pub struct ColorSpec {
1414 fg_color: Option<Color>,
1415 bg_color: Option<Color>,
1416 bold: bool,
1417 intense: bool,
1418 underline: bool,
1419}
1420
1421impl ColorSpec {
1422 pub fn new() -> ColorSpec {
1424 ColorSpec::default()
1425 }
1426
1427 pub fn fg(&self) -> Option<&Color> { self.fg_color.as_ref() }
1429
1430 pub fn set_fg(&mut self, color: Option<Color>) -> &mut ColorSpec {
1432 self.fg_color = color;
1433 self
1434 }
1435
1436 pub fn bg(&self) -> Option<&Color> { self.bg_color.as_ref() }
1438
1439 pub fn set_bg(&mut self, color: Option<Color>) -> &mut ColorSpec {
1441 self.bg_color = color;
1442 self
1443 }
1444
1445 pub fn bold(&self) -> bool { self.bold }
1449
1450 pub fn set_bold(&mut self, yes: bool) -> &mut ColorSpec {
1454 self.bold = yes;
1455 self
1456 }
1457
1458 pub fn underline(&self) -> bool { self.underline }
1462
1463 pub fn set_underline(&mut self, yes: bool) -> &mut ColorSpec {
1467 self.underline = yes;
1468 self
1469 }
1470
1471 pub fn intense(&self) -> bool { self.intense }
1480
1481 pub fn set_intense(&mut self, yes: bool) -> &mut ColorSpec {
1490 self.intense = yes;
1491 self
1492 }
1493
1494 pub fn is_none(&self) -> bool {
1496 self.fg_color.is_none() && self.bg_color.is_none()
1497 && !self.bold && !self.underline
1498 }
1499
1500 pub fn clear(&mut self) {
1502 self.fg_color = None;
1503 self.bg_color = None;
1504 self.bold = false;
1505 self.underline = false;
1506 }
1507
1508 #[cfg(windows)]
1510 fn write_console(
1511 &self,
1512 console: &mut wincolor::Console,
1513 ) -> io::Result<()> {
1514 let fg_color = self.fg_color.and_then(|c| c.to_windows(self.intense));
1515 if let Some((intense, color)) = fg_color {
1516 console.fg(intense, color)?;
1517 }
1518 let bg_color = self.bg_color.and_then(|c| c.to_windows(self.intense));
1519 if let Some((intense, color)) = bg_color {
1520 console.bg(intense, color)?;
1521 }
1522 Ok(())
1523 }
1524}
1525
1526#[allow(missing_docs)]
1548#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1549pub enum Color {
1550 Black,
1551 Blue,
1552 Green,
1553 Red,
1554 Cyan,
1555 Magenta,
1556 Yellow,
1557 White,
1558 Ansi256(u8),
1559 Rgb(u8, u8, u8),
1560 #[doc(hidden)]
1561 __Nonexhaustive,
1562}
1563
1564impl Color {
1565 #[cfg(windows)]
1567 fn to_windows(
1568 self,
1569 intense: bool,
1570 ) -> Option<(wincolor::Intense, wincolor::Color)> {
1571 use wincolor::Intense::{Yes, No};
1572
1573 let color = match self {
1574 Color::Black => wincolor::Color::Black,
1575 Color::Blue => wincolor::Color::Blue,
1576 Color::Green => wincolor::Color::Green,
1577 Color::Red => wincolor::Color::Red,
1578 Color::Cyan => wincolor::Color::Cyan,
1579 Color::Magenta => wincolor::Color::Magenta,
1580 Color::Yellow => wincolor::Color::Yellow,
1581 Color::White => wincolor::Color::White,
1582 Color::Ansi256(0) => return Some((No, wincolor::Color::Black)),
1583 Color::Ansi256(1) => return Some((No, wincolor::Color::Red)),
1584 Color::Ansi256(2) => return Some((No, wincolor::Color::Green)),
1585 Color::Ansi256(3) => return Some((No, wincolor::Color::Yellow)),
1586 Color::Ansi256(4) => return Some((No, wincolor::Color::Blue)),
1587 Color::Ansi256(5) => return Some((No, wincolor::Color::Magenta)),
1588 Color::Ansi256(6) => return Some((No, wincolor::Color::Cyan)),
1589 Color::Ansi256(7) => return Some((No, wincolor::Color::White)),
1590 Color::Ansi256(8) => return Some((Yes, wincolor::Color::Black)),
1591 Color::Ansi256(9) => return Some((Yes, wincolor::Color::Red)),
1592 Color::Ansi256(10) => return Some((Yes, wincolor::Color::Green)),
1593 Color::Ansi256(11) => return Some((Yes, wincolor::Color::Yellow)),
1594 Color::Ansi256(12) => return Some((Yes, wincolor::Color::Blue)),
1595 Color::Ansi256(13) => return Some((Yes, wincolor::Color::Magenta)),
1596 Color::Ansi256(14) => return Some((Yes, wincolor::Color::Cyan)),
1597 Color::Ansi256(15) => return Some((Yes, wincolor::Color::White)),
1598 Color::Ansi256(_) => return None,
1599 Color::Rgb(_, _, _) => return None,
1600 Color::__Nonexhaustive => unreachable!(),
1601 };
1602 let intense = if intense { Yes } else { No };
1603 Some((intense, color))
1604 }
1605
1606 fn from_str_numeric(s: &str) -> Result<Color, ParseColorError> {
1608 fn parse_number(s: &str) -> Option<u8> {
1615 use std::u8;
1616
1617 if s.starts_with("0x") {
1618 u8::from_str_radix(&s[2..], 16).ok()
1619 } else {
1620 u8::from_str_radix(s, 10).ok()
1621 }
1622 }
1623
1624 let codes: Vec<&str> = s.split(',').collect();
1625 if codes.len() == 1 {
1626 if let Some(n) = parse_number(&codes[0]) {
1627 Ok(Color::Ansi256(n))
1628 } else {
1629 if s.chars().all(|c| c.is_digit(16)) {
1630 Err(ParseColorError {
1631 kind: ParseColorErrorKind::InvalidAnsi256,
1632 given: s.to_string(),
1633 })
1634 } else {
1635 Err(ParseColorError {
1636 kind: ParseColorErrorKind::InvalidName,
1637 given: s.to_string(),
1638 })
1639 }
1640 }
1641 } else if codes.len() == 3 {
1642 let mut v = vec![];
1643 for code in codes {
1644 let n = parse_number(code).ok_or_else(|| {
1645 ParseColorError {
1646 kind: ParseColorErrorKind::InvalidRgb,
1647 given: s.to_string(),
1648 }
1649 })?;
1650 v.push(n);
1651 }
1652 Ok(Color::Rgb(v[0], v[1], v[2]))
1653 } else {
1654 Err(if s.contains(",") {
1655 ParseColorError {
1656 kind: ParseColorErrorKind::InvalidRgb,
1657 given: s.to_string(),
1658 }
1659 } else {
1660 ParseColorError {
1661 kind: ParseColorErrorKind::InvalidName,
1662 given: s.to_string(),
1663 }
1664 })
1665 }
1666 }
1667}
1668
1669#[derive(Clone, Debug, Eq, PartialEq)]
1671pub struct ParseColorError {
1672 kind: ParseColorErrorKind,
1673 given: String,
1674}
1675
1676#[derive(Clone, Debug, Eq, PartialEq)]
1677enum ParseColorErrorKind {
1678 InvalidName,
1679 InvalidAnsi256,
1680 InvalidRgb,
1681}
1682
1683impl ParseColorError {
1684 pub fn invalid(&self) -> &str { &self.given }
1686}
1687
1688impl error::Error for ParseColorError {
1689 fn description(&self) -> &str {
1690 use self::ParseColorErrorKind::*;
1691 match self.kind {
1692 InvalidName => "unrecognized color name",
1693 InvalidAnsi256 => "invalid ansi256 color number",
1694 InvalidRgb => "invalid RGB color triple",
1695 }
1696 }
1697}
1698
1699impl fmt::Display for ParseColorError {
1700 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1701 use self::ParseColorErrorKind::*;
1702 match self.kind {
1703 InvalidName => {
1704 write!(f, "unrecognized color name '{}'. Choose from: \
1705 black, blue, green, red, cyan, magenta, yellow, \
1706 white",
1707 self.given)
1708 }
1709 InvalidAnsi256 => {
1710 write!(f, "unrecognized ansi256 color number, \
1711 should be '[0-255]' (or a hex number), but is '{}'",
1712 self.given)
1713 }
1714 InvalidRgb => {
1715 write!(f, "unrecognized RGB color triple, \
1716 should be '[0-255],[0-255],[0-255]' (or a hex \
1717 triple), but is '{}'", self.given)
1718 }
1719 }
1720 }
1721}
1722
1723impl FromStr for Color {
1724 type Err = ParseColorError;
1725
1726 fn from_str(s: &str) -> Result<Color, ParseColorError> {
1727 match &*s.to_lowercase() {
1728 "black" => Ok(Color::Black),
1729 "blue" => Ok(Color::Blue),
1730 "green" => Ok(Color::Green),
1731 "red" => Ok(Color::Red),
1732 "cyan" => Ok(Color::Cyan),
1733 "magenta" => Ok(Color::Magenta),
1734 "yellow" => Ok(Color::Yellow),
1735 "white" => Ok(Color::White),
1736 _ => Color::from_str_numeric(s),
1737 }
1738 }
1739}
1740
1741struct LossyStandardStream<W> {
1742 wtr: W,
1743 #[cfg(windows)]
1744 is_console: bool,
1745}
1746
1747impl<W: io::Write> LossyStandardStream<W> {
1748 #[cfg(not(windows))]
1749 fn new(wtr: W) -> LossyStandardStream<W> {
1750 LossyStandardStream { wtr: wtr }
1751 }
1752
1753 #[cfg(windows)]
1754 fn new(wtr: W) -> LossyStandardStream<W> {
1755 let is_console =
1756 wincolor::Console::stdout().is_ok()
1757 || wincolor::Console::stderr().is_ok();
1758 LossyStandardStream { wtr: wtr, is_console: is_console }
1759 }
1760
1761 #[cfg(not(windows))]
1762 fn wrap<Q: io::Write>(&self, wtr: Q) -> LossyStandardStream<Q> {
1763 LossyStandardStream::new(wtr)
1764 }
1765
1766 #[cfg(windows)]
1767 fn wrap<Q: io::Write>(&self, wtr: Q) -> LossyStandardStream<Q> {
1768 LossyStandardStream { wtr: wtr, is_console: self.is_console }
1769 }
1770
1771 fn get_ref(&self) -> &W {
1772 &self.wtr
1773 }
1774}
1775
1776impl<W: WriteColor> WriteColor for LossyStandardStream<W> {
1777 fn supports_color(&self) -> bool { self.wtr.supports_color() }
1778 fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
1779 self.wtr.set_color(spec)
1780 }
1781 fn reset(&mut self) -> io::Result<()> { self.wtr.reset() }
1782 fn is_synchronous(&self) -> bool { self.wtr.is_synchronous() }
1783}
1784
1785impl<W: io::Write> io::Write for LossyStandardStream<W> {
1786 #[cfg(not(windows))]
1787 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1788 self.wtr.write(buf)
1789 }
1790
1791 #[cfg(windows)]
1792 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1793 if self.is_console {
1794 write_lossy_utf8(&mut self.wtr, buf)
1795 } else {
1796 self.wtr.write(buf)
1797 }
1798 }
1799
1800 fn flush(&mut self) -> io::Result<()> {
1801 self.wtr.flush()
1802 }
1803}
1804
1805#[cfg(windows)]
1806fn write_lossy_utf8<W: io::Write>(mut w: W, buf: &[u8]) -> io::Result<usize> {
1807 match ::std::str::from_utf8(buf) {
1808 Ok(s) => w.write(s.as_bytes()),
1809 Err(ref e) if e.valid_up_to() == 0 => {
1810 w.write(b"\xEF\xBF\xBD")?;
1811 Ok(1)
1812 }
1813 Err(e) => w.write(&buf[..e.valid_up_to()]),
1814 }
1815}
1816
1817#[cfg(test)]
1818mod tests {
1819 use super::{
1820 Ansi, Color, ParseColorError, ParseColorErrorKind, StandardStream,
1821 };
1822
1823 fn assert_is_send<T: Send>() {}
1824
1825 #[test]
1826 fn standard_stream_is_send() {
1827 assert_is_send::<StandardStream>();
1828 }
1829
1830 #[test]
1831 fn test_simple_parse_ok() {
1832 let color = "green".parse::<Color>();
1833 assert_eq!(color, Ok(Color::Green));
1834 }
1835
1836 #[test]
1837 fn test_256_parse_ok() {
1838 let color = "7".parse::<Color>();
1839 assert_eq!(color, Ok(Color::Ansi256(7)));
1840
1841 let color = "32".parse::<Color>();
1842 assert_eq!(color, Ok(Color::Ansi256(32)));
1843
1844 let color = "0xFF".parse::<Color>();
1845 assert_eq!(color, Ok(Color::Ansi256(0xFF)));
1846 }
1847
1848 #[test]
1849 fn test_256_parse_err_out_of_range() {
1850 let color = "256".parse::<Color>();
1851 assert_eq!(color, Err(ParseColorError {
1852 kind: ParseColorErrorKind::InvalidAnsi256,
1853 given: "256".to_string(),
1854 }));
1855 }
1856
1857 #[test]
1858 fn test_rgb_parse_ok() {
1859 let color = "0,0,0".parse::<Color>();
1860 assert_eq!(color, Ok(Color::Rgb(0, 0, 0)));
1861
1862 let color = "0,128,255".parse::<Color>();
1863 assert_eq!(color, Ok(Color::Rgb(0, 128, 255)));
1864
1865 let color = "0x0,0x0,0x0".parse::<Color>();
1866 assert_eq!(color, Ok(Color::Rgb(0, 0, 0)));
1867
1868 let color = "0x33,0x66,0xFF".parse::<Color>();
1869 assert_eq!(color, Ok(Color::Rgb(0x33, 0x66, 0xFF)));
1870 }
1871
1872 #[test]
1873 fn test_rgb_parse_err_out_of_range() {
1874 let color = "0,0,256".parse::<Color>();
1875 assert_eq!(color, Err(ParseColorError {
1876 kind: ParseColorErrorKind::InvalidRgb,
1877 given: "0,0,256".to_string(),
1878 }));
1879 }
1880
1881 #[test]
1882 fn test_rgb_parse_err_bad_format() {
1883 let color = "0,0".parse::<Color>();
1884 assert_eq!(color, Err(ParseColorError {
1885 kind: ParseColorErrorKind::InvalidRgb,
1886 given: "0,0".to_string(),
1887 }));
1888
1889 let color = "not_a_color".parse::<Color>();
1890 assert_eq!(color, Err(ParseColorError {
1891 kind: ParseColorErrorKind::InvalidName,
1892 given: "not_a_color".to_string(),
1893 }));
1894 }
1895
1896 #[test]
1897 fn test_var_ansi_write_rgb() {
1898 let mut buf = Ansi::new(vec![]);
1899 let _ = buf.write_color(true, &Color::Rgb(254, 253, 255), false);
1900 assert_eq!(buf.0, b"\x1B[38;2;254;253;255m");
1901 }
1902
1903 #[test]
1904 fn test_var_ansi_write_256() {
1905 let mut buf = Ansi::new(vec![]);
1906 let _ = buf.write_color(false, &Color::Ansi256(7), false);
1907 assert_eq!(buf.0, b"\x1B[48;5;7m");
1908
1909 let mut buf = Ansi::new(vec![]);
1910 let _ = buf.write_color(false, &Color::Ansi256(208), false);
1911 assert_eq!(buf.0, b"\x1B[48;5;208m");
1912 }
1913}