// #[cfg(doctest)] // use doc_comment::doctest; // #[cfg(doctest)] // doctest!("../README.md");
use std::env; use std::error; use std::fmt; use std::io::{self, Write}; use std::str::FromStr; use std::sync::atomic::{AtomicBool, Ordering}; #[cfg(windows)] use std::sync::{Mutex, MutexGuard};
#[cfg(windows)] use winapi_util::console as wincon;
/// This trait describes the behavior of writers that support colored output. pubtrait WriteColor: io::Write { /// Returns true if and only if the underlying writer supports colors. fn supports_color(&self) -> bool;
/// Set the color settings of the writer. /// /// Subsequent writes to this writer will use these settings until either /// `reset` is called or new color settings are set. /// /// If there was a problem setting the color settings, then an error is /// returned. fn set_color(&mutself, spec: &ColorSpec) -> io::Result<()>;
/// Reset the current color settings to their original settings. /// /// If there was a problem resetting the color settings, then an error is /// returned. /// /// Note that this does not reset hyperlinks. Those need to be /// reset on their own, e.g., by calling `set_hyperlink` with /// [`HyperlinkSpec::none`]. fn reset(&mutself) -> io::Result<()>;
/// Returns true if and only if the underlying writer must synchronously /// interact with an end user's device in order to control colors. By /// default, this always returns `false`. /// /// In practice, this should return `true` if the underlying writer is /// manipulating colors using the Windows console APIs. /// /// This is useful for writing generic code (such as a buffered writer) /// that can perform certain optimizations when the underlying writer /// doesn't rely on synchronous APIs. For example, ANSI escape sequences /// can be passed through to the end user's device as is. fn is_synchronous(&self) -> bool { false
}
/// Set the current hyperlink of the writer. /// /// The typical way to use this is to first call it with a /// [`HyperlinkSpec::open`] to write the actual URI to a tty that supports /// [OSC-8]. At this point, the caller can now write the label for the /// hyperlink. This may include coloring or other styles. Once the caller /// has finished writing the label, one should call this method again with /// [`HyperlinkSpec::close`]. /// /// If there was a problem setting the hyperlink, then an error is /// returned. /// /// This defaults to doing nothing. /// /// [OSC8]: https://github.com/Alhadis/OSC8-Adoption/ fn set_hyperlink(&mutself, _link: &HyperlinkSpec) -> io::Result<()> {
Ok(())
}
/// Returns true if and only if the underlying writer supports hyperlinks. /// /// This can be used to avoid generating hyperlink URIs unnecessarily. /// /// This defaults to `false`. fn supports_hyperlinks(&self) -> bool { false
}
}
/// ColorChoice represents the color preferences of an end user. /// /// The `Default` implementation for this type will select `Auto`, which tries /// to do the right thing based on the current environment. /// /// The `FromStr` implementation for this type converts a lowercase kebab-case /// string of the variant name to the corresponding variant. Any other string /// results in an error. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pubenum ColorChoice { /// Try very hard to emit colors. This includes emitting ANSI colors /// on Windows if the console API is unavailable.
Always, /// AlwaysAnsi is like Always, except it never tries to use anything other /// than emitting ANSI color codes.
AlwaysAnsi, /// Try to use colors, but don't force the issue. If the console isn't /// available on Windows, or if TERM=dumb, or if `NO_COLOR` is defined, for /// example, then don't use colors.
Auto, /// Never emit colors.
Never,
}
/// The default is `Auto`. impl Default for ColorChoice { fn default() -> ColorChoice {
ColorChoice::Auto
}
}
impl FromStr for ColorChoice { type Err = ColorChoiceParseError;
impl ColorChoice { /// Returns true if we should attempt to write colored output. fn should_attempt_color(&self) -> bool { match *self {
ColorChoice::Always => true,
ColorChoice::AlwaysAnsi => true,
ColorChoice::Never => false,
ColorChoice::Auto => self.env_allows_color(),
}
}
#[cfg(not(windows))] fn env_allows_color(&self) -> bool { match env::var_os("TERM") { // If TERM isn't set, then we are in a weird environment that // probably doesn't support colors.
None => returnfalse,
Some(k) => { if k == "dumb" { returnfalse;
}
}
} // If TERM != dumb, then the only way we don't allow colors at this // point is if NO_COLOR is set. if env::var_os("NO_COLOR").is_some() { returnfalse;
} true
}
#[cfg(windows)] fn env_allows_color(&self) -> bool { // On Windows, if TERM isn't set, then we shouldn't automatically // assume that colors aren't allowed. This is unlike Unix environments // where TERM is more rigorously set. iflet Some(k) = env::var_os("TERM") { if k == "dumb" { returnfalse;
}
} // If TERM != dumb, then the only way we don't allow colors at this // point is if NO_COLOR is set. if env::var_os("NO_COLOR").is_some() { returnfalse;
} true
}
/// Returns true if this choice should forcefully use ANSI color codes. /// /// It's possible that ANSI is still the correct choice even if this /// returns false. #[cfg(windows)] fn should_ansi(&self) -> bool { match *self {
ColorChoice::Always => false,
ColorChoice::AlwaysAnsi => true,
ColorChoice::Never => false,
ColorChoice::Auto => { match env::var("TERM") {
Err(_) => false, // cygwin doesn't seem to support ANSI escape sequences // and instead has its own variety. However, the Windows // console API may be available.
Ok(k) => k != "dumb" && k != "cygwin",
}
}
}
}
}
/// An error that occurs when parsing a `ColorChoice` fails. #[derive(Clone, Debug)] pubstruct ColorChoiceParseError {
unknown_choice: String,
}
impl std::error::Error for ColorChoiceParseError {}
/// `std::io` implements `Stdout` and `Stderr` (and their `Lock` variants) as /// separate types, which makes it difficult to abstract over them. We use /// some simple internal enum types to work around this.
fn lock(&self) -> IoStandardStreamLock<'_> { match *self {
IoStandardStream::Stdout(ref s) => {
IoStandardStreamLock::StdoutLock(s.lock())
}
IoStandardStream::Stderr(ref s) => {
IoStandardStreamLock::StderrLock(s.lock())
}
IoStandardStream::StdoutBuffered(_)
| IoStandardStream::StderrBuffered(_) => { // We don't permit this case to ever occur in the public API, // so it's OK to panic.
panic!("cannot lock a buffered standard stream")
}
}
}
}
/// Satisfies `io::Write` and `WriteColor`, and supports optional coloring /// to either of the standard output streams, stdout and stderr. #[derive(Debug)] pubstruct StandardStream {
wtr: LossyStandardStream<WriterInner<IoStandardStream>>,
}
/// `StandardStreamLock` is a locked reference to a `StandardStream`. /// /// This implements the `io::Write` and `WriteColor` traits, and is constructed /// via the `Write::lock` method. /// /// The lifetime `'a` refers to the lifetime of the corresponding /// `StandardStream`. #[derive(Debug)] pubstruct StandardStreamLock<'a> {
wtr: LossyStandardStream<WriterInnerLock<'a, IoStandardStreamLock<'a>>>,
}
/// Like `StandardStream`, but does buffered writing. #[derive(Debug)] pubstruct BufferedStandardStream {
wtr: LossyStandardStream<WriterInner<IoStandardStream>>,
}
/// WriterInner is a (limited) generic representation of a writer. It is /// limited because W should only ever be stdout/stderr on Windows. #[derive(Debug)] enum WriterInner<W> {
NoColor(NoColor<W>),
Ansi(Ansi<W>), #[cfg(windows)]
Windows {
wtr: W,
console: Mutex<wincon::Console>,
},
}
/// WriterInnerLock is a (limited) generic representation of a writer. It is /// limited because W should only ever be stdout/stderr on Windows. #[derive(Debug)] enum WriterInnerLock<'a, W> {
NoColor(NoColor<W>),
Ansi(Ansi<W>), /// What a gross hack. On Windows, we need to specify a lifetime for the /// console when in a locked state, but obviously don't need to do that /// on Unix, which makes the `'a` unused. To satisfy the compiler, we need /// a PhantomData. #[allow(dead_code)]
Unreachable(::std::marker::PhantomData<&'a ()>), #[cfg(windows)]
Windows {
wtr: W,
console: MutexGuard<'a, wincon::Console>,
},
}
impl StandardStream { /// Create a new `StandardStream` with the given color preferences that /// writes to standard output. /// /// On Windows, if coloring is desired and a Windows console could not be /// found, then ANSI escape sequences are used instead. /// /// The specific color/style settings can be configured when writing via /// the `WriteColor` trait. pubfn stdout(choice: ColorChoice) -> StandardStream { let wtr = WriterInner::create(StandardStreamType::Stdout, choice);
StandardStream { wtr: LossyStandardStream::new(wtr) }
}
/// Create a new `StandardStream` with the given color preferences that /// writes to standard error. /// /// On Windows, if coloring is desired and a Windows console could not be /// found, then ANSI escape sequences are used instead. /// /// The specific color/style settings can be configured when writing via /// the `WriteColor` trait. pubfn stderr(choice: ColorChoice) -> StandardStream { let wtr = WriterInner::create(StandardStreamType::Stderr, choice);
StandardStream { wtr: LossyStandardStream::new(wtr) }
}
/// Lock the underlying writer. /// /// The lock guard returned also satisfies `io::Write` and /// `WriteColor`. /// /// This method is **not reentrant**. It may panic if `lock` is called /// while a `StandardStreamLock` is still alive. pubfn lock(&self) -> StandardStreamLock<'_> {
StandardStreamLock::from_stream(self)
}
}
impl BufferedStandardStream { /// Create a new `BufferedStandardStream` with the given color preferences /// that writes to standard output via a buffered writer. /// /// On Windows, if coloring is desired and a Windows console could not be /// found, then ANSI escape sequences are used instead. /// /// The specific color/style settings can be configured when writing via /// the `WriteColor` trait. pubfn stdout(choice: ColorChoice) -> BufferedStandardStream { let wtr =
WriterInner::create(StandardStreamType::StdoutBuffered, choice);
BufferedStandardStream { wtr: LossyStandardStream::new(wtr) }
}
/// Create a new `BufferedStandardStream` with the given color preferences /// that writes to standard error via a buffered writer. /// /// On Windows, if coloring is desired and a Windows console could not be /// found, then ANSI escape sequences are used instead. /// /// The specific color/style settings can be configured when writing via /// the `WriteColor` trait. pubfn stderr(choice: ColorChoice) -> BufferedStandardStream { let wtr =
WriterInner::create(StandardStreamType::StderrBuffered, choice);
BufferedStandardStream { wtr: LossyStandardStream::new(wtr) }
}
}
impl WriterInner<IoStandardStream> { /// Create a new inner writer for a standard stream with the given color /// preferences. #[cfg(not(windows))] fn create(
sty: StandardStreamType,
choice: ColorChoice,
) -> WriterInner<IoStandardStream> { if choice.should_attempt_color() {
WriterInner::Ansi(Ansi(IoStandardStream::new(sty)))
} else {
WriterInner::NoColor(NoColor(IoStandardStream::new(sty)))
}
}
/// Create a new inner writer for a standard stream with the given color /// preferences. /// /// If coloring is desired and a Windows console could not be found, then /// ANSI escape sequences are used instead. #[cfg(windows)] fn create(
sty: StandardStreamType,
choice: ColorChoice,
) -> WriterInner<IoStandardStream> { letmut con = match sty {
StandardStreamType::Stdout => wincon::Console::stdout(),
StandardStreamType::Stderr => wincon::Console::stderr(),
StandardStreamType::StdoutBuffered => wincon::Console::stdout(),
StandardStreamType::StderrBuffered => wincon::Console::stderr(),
}; let is_console_virtual = con
.as_mut()
.map(|con| con.set_virtual_terminal_processing(true).is_ok())
.unwrap_or(false); if choice.should_attempt_color() { if choice.should_ansi() || is_console_virtual {
WriterInner::Ansi(Ansi(IoStandardStream::new(sty)))
} elseiflet Ok(console) = con {
WriterInner::Windows {
wtr: IoStandardStream::new(sty),
console: Mutex::new(console),
}
} else {
WriterInner::Ansi(Ansi(IoStandardStream::new(sty)))
}
} else {
WriterInner::NoColor(NoColor(IoStandardStream::new(sty)))
}
}
}
/// Writes colored buffers to stdout or stderr. /// /// Writable buffers can be obtained by calling `buffer` on a `BufferWriter`. /// /// This writer works with terminals that support ANSI escape sequences or /// with a Windows console. /// /// It is intended for a `BufferWriter` to be put in an `Arc` and written to /// from multiple threads simultaneously. #[derive(Debug)] pubstruct BufferWriter {
stream: LossyStandardStream<IoStandardStream>,
printed: AtomicBool,
separator: Option<Vec<u8>>,
color_choice: ColorChoice, #[cfg(windows)]
console: Option<Mutex<wincon::Console>>,
}
impl BufferWriter { /// Create a new `BufferWriter` that writes to a standard stream with the /// given color preferences. /// /// The specific color/style settings can be configured when writing to /// the buffers themselves. #[cfg(not(windows))] fn create(sty: StandardStreamType, choice: ColorChoice) -> BufferWriter {
BufferWriter {
stream: LossyStandardStream::new(IoStandardStream::new(sty)),
printed: AtomicBool::new(false),
separator: None,
color_choice: choice,
}
}
/// Create a new `BufferWriter` that writes to a standard stream with the /// given color preferences. /// /// If coloring is desired and a Windows console could not be found, then /// ANSI escape sequences are used instead. /// /// The specific color/style settings can be configured when writing to /// the buffers themselves. #[cfg(windows)] fn create(sty: StandardStreamType, choice: ColorChoice) -> BufferWriter { letmut con = match sty {
StandardStreamType::Stdout => wincon::Console::stdout(),
StandardStreamType::Stderr => wincon::Console::stderr(),
StandardStreamType::StdoutBuffered => wincon::Console::stdout(),
StandardStreamType::StderrBuffered => wincon::Console::stderr(),
}
.ok(); let is_console_virtual = con
.as_mut()
.map(|con| con.set_virtual_terminal_processing(true).is_ok())
.unwrap_or(false); // If we can enable ANSI on Windows, then we don't need the console // anymore. if is_console_virtual {
con = None;
} let stream = LossyStandardStream::new(IoStandardStream::new(sty));
BufferWriter {
stream,
printed: AtomicBool::new(false),
separator: None,
color_choice: choice,
console: con.map(Mutex::new),
}
}
/// Create a new `BufferWriter` that writes to stdout with the given /// color preferences. /// /// On Windows, if coloring is desired and a Windows console could not be /// found, then ANSI escape sequences are used instead. /// /// The specific color/style settings can be configured when writing to /// the buffers themselves. pubfn stdout(choice: ColorChoice) -> BufferWriter {
BufferWriter::create(StandardStreamType::Stdout, choice)
}
/// Create a new `BufferWriter` that writes to stderr with the given /// color preferences. /// /// On Windows, if coloring is desired and a Windows console could not be /// found, then ANSI escape sequences are used instead. /// /// The specific color/style settings can be configured when writing to /// the buffers themselves. pubfn stderr(choice: ColorChoice) -> BufferWriter {
BufferWriter::create(StandardStreamType::Stderr, choice)
}
/// If set, the separator given is printed between buffers. By default, no /// separator is printed. /// /// The default value is `None`. pubfn separator(&mutself, sep: Option<Vec<u8>>) { self.separator = sep;
}
/// Creates a new `Buffer` with the current color preferences. /// /// A `Buffer` satisfies both `io::Write` and `WriteColor`. A `Buffer` can /// be printed using the `print` method. #[cfg(not(windows))] pubfn buffer(&self) -> Buffer {
Buffer::new(self.color_choice)
}
/// Creates a new `Buffer` with the current color preferences. /// /// A `Buffer` satisfies both `io::Write` and `WriteColor`. A `Buffer` can /// be printed using the `print` method. #[cfg(windows)] pubfn buffer(&self) -> Buffer {
Buffer::new(self.color_choice, self.console.is_some())
}
/// Prints the contents of the given buffer. /// /// It is safe to call this from multiple threads simultaneously. In /// particular, all buffers are written atomically. No interleaving will /// occur. pubfn print(&self, buf: &Buffer) -> io::Result<()> { if buf.is_empty() { return Ok(());
} letmut stream = self.stream.wrap(self.stream.get_ref().lock()); iflet Some(ref sep) = self.separator { ifself.printed.load(Ordering::Relaxed) {
stream.write_all(sep)?;
stream.write_all(b"\n")?;
}
} match buf.0 {
BufferInner::NoColor(ref b) => stream.write_all(&b.0)?,
BufferInner::Ansi(ref b) => stream.write_all(&b.0)?, #[cfg(windows)]
BufferInner::Windows(ref b) => { // We guarantee by construction that we have a console here. // Namely, a BufferWriter is the only way to produce a Buffer. let console_mutex = self
.console
.as_ref()
.expect("got Windows buffer but have no Console"); letmut console = console_mutex.lock().unwrap();
b.print(&mut *console, &mut stream)?;
}
} self.printed.store(true, Ordering::Relaxed);
Ok(())
}
}
/// Write colored text to memory. /// /// `Buffer` is a platform independent abstraction for printing colored text to /// an in memory buffer. When the buffer is printed using a `BufferWriter`, the /// color information will be applied to the output device (a tty on Unix and a /// console on Windows). /// /// A `Buffer` is typically created by calling the `BufferWriter.buffer` /// method, which will take color preferences and the environment into /// account. However, buffers can also be manually created using `no_color`, /// `ansi` or `console` (on Windows). #[derive(Clone, Debug)] pubstruct Buffer(BufferInner);
/// BufferInner is an enumeration of different buffer types. #[derive(Clone, Debug)] enum BufferInner { /// No coloring information should be applied. This ignores all coloring /// directives.
NoColor(NoColor<Vec<u8>>), /// Apply coloring using ANSI escape sequences embedded into the buffer.
Ansi(Ansi<Vec<u8>>), /// Apply coloring using the Windows console APIs. This buffer saves /// color information in memory and only interacts with the console when /// the buffer is printed. #[cfg(windows)]
Windows(WindowsBuffer),
}
impl Buffer { /// Create a new buffer with the given color settings. #[cfg(not(windows))] fn new(choice: ColorChoice) -> Buffer { if choice.should_attempt_color() {
Buffer::ansi()
} else {
Buffer::no_color()
}
}
/// Create a new buffer with the given color settings. /// /// On Windows, one can elect to create a buffer capable of being written /// to a console. Only enable it if a console is available. /// /// If coloring is desired and `console` is false, then ANSI escape /// sequences are used instead. #[cfg(windows)] fn new(choice: ColorChoice, console: bool) -> Buffer { if choice.should_attempt_color() { if !console || choice.should_ansi() {
Buffer::ansi()
} else {
Buffer::console()
}
} else {
Buffer::no_color()
}
}
/// Create a buffer that drops all color information. pubfn no_color() -> Buffer {
Buffer(BufferInner::NoColor(NoColor(vec![])))
}
/// Create a buffer that uses ANSI escape sequences. pubfn ansi() -> Buffer {
Buffer(BufferInner::Ansi(Ansi(vec![])))
}
/// Create a buffer that can be written to a Windows console. #[cfg(windows)] pubfn console() -> Buffer {
Buffer(BufferInner::Windows(WindowsBuffer::new()))
}
/// Returns true if and only if this buffer is empty. pubfn is_empty(&self) -> bool { self.len() == 0
}
/// Returns the length of this buffer in bytes. pubfn len(&self) -> usize { matchself.0 {
BufferInner::NoColor(ref b) => b.0.len(),
BufferInner::Ansi(ref b) => b.0.len(), #[cfg(windows)]
BufferInner::Windows(ref b) => b.buf.len(),
}
}
/// Clears this buffer. pubfn clear(&mutself) { matchself.0 {
BufferInner::NoColor(refmut b) => b.0.clear(),
BufferInner::Ansi(refmut b) => b.0.clear(), #[cfg(windows)]
BufferInner::Windows(refmut b) => b.clear(),
}
}
/// Consume this buffer and return the underlying raw data. /// /// On Windows, this unrecoverably drops all color information associated /// with the buffer. pubfn into_inner(self) -> Vec<u8> { matchself.0 {
BufferInner::NoColor(b) => b.0,
BufferInner::Ansi(b) => b.0, #[cfg(windows)]
BufferInner::Windows(b) => b.buf,
}
}
/// Return the underlying data of the buffer. pubfn as_slice(&self) -> &[u8] { matchself.0 {
BufferInner::NoColor(ref b) => &b.0,
BufferInner::Ansi(ref b) => &b.0, #[cfg(windows)]
BufferInner::Windows(ref b) => &b.buf,
}
}
/// Return the underlying data of the buffer as a mutable slice. pubfn as_mut_slice(&mutself) -> &mut [u8] { matchself.0 {
BufferInner::NoColor(refmut b) => &mut b.0,
BufferInner::Ansi(refmut b) => &mut b.0, #[cfg(windows)]
BufferInner::Windows(refmut b) => &mut b.buf,
}
}
}
/// Satisfies `WriteColor` but ignores all color options. #[derive(Clone, Debug)] pubstruct NoColor<W>(W);
impl<W: Write> NoColor<W> { /// Create a new writer that satisfies `WriteColor` but drops all color /// information. pubfn new(wtr: W) -> NoColor<W> {
NoColor(wtr)
}
/// Consume this `NoColor` value and return the inner writer. pubfn into_inner(self) -> W { self.0
}
/// Return a reference to the inner writer. pubfn get_ref(&self) -> &W {
&self.0
}
/// Return a mutable reference to the inner writer. pubfn get_mut(&mutself) -> &mut W {
&mutself.0
}
}
// Adding this method here is not required because it has a default impl, // but it seems to provide a perf improvement in some cases when using // a `BufWriter` with lots of writes. // // See https://github.com/BurntSushi/termcolor/pull/56 for more details // and a minimized example. #[inline] fn write_all(&mutself, buf: &[u8]) -> io::Result<()> { self.0.write_all(buf)
}
fn write_color(
&mutself,
fg: bool,
c: &Color,
intense: bool,
) -> io::Result<()> {
macro_rules! write_intense {
($clr:expr) => { if fg { self.write_str(concat!("\x1B[38;5;", $clr, "m"))
} else { self.write_str(concat!("\x1B[48;5;", $clr, "m"))
}
};
}
macro_rules! write_normal {
($clr:expr) => { if fg { self.write_str(concat!("\x1B[3", $clr, "m"))
} else { self.write_str(concat!("\x1B[4", $clr, "m"))
}
};
}
macro_rules! write_var_ansi_code {
($pre:expr, $($code:expr),+) => {{ // The loop generates at worst a literal of the form // '255,255,255m' which is 12-bytes. // The largest `pre` expression we currently use is 7 bytes. // This gives us the maximum of 19-bytes for our work buffer. let pre_len = $pre.len();
assert!(pre_len <= 7); letmut fmt = [0u8; 19];
fmt[..pre_len].copy_from_slice($pre); letmut i = pre_len - 1;
$( let c1: u8 = ($code / 100) % 10; let c2: u8 = ($code / 10) % 10; let c3: u8 = $code % 10; letmut printed = false;
if c1 != 0 {
printed = true;
i += 1;
fmt[i] = b'0' + c1;
} if c2 != 0 || printed {
i += 1;
fmt[i] = b'0' + c2;
} // If we received a zero value we must still print a value.
i += 1;
fmt[i] = b'0' + c3;
i += 1;
fmt[i] = b';';
)+
/// An in-memory buffer that provides Windows console coloring. /// /// This doesn't actually communicate with the Windows console. Instead, it /// acts like a normal buffer but also saves the color information associated /// with positions in the buffer. It is only when the buffer is written to the /// console that coloring is actually applied. /// /// This is roughly isomorphic to the ANSI based approach (i.e., /// `Ansi<Vec<u8>>`), except with ANSI, the color information is embedded /// directly into the buffer. /// /// Note that there is no way to write something generic like /// `WindowsConsole<W: io::Write>` since coloring on Windows is tied /// specifically to the console APIs, and therefore can't work on arbitrary /// writers. #[cfg(windows)] #[derive(Clone, Debug)] struct WindowsBuffer { /// The actual content that should be printed.
buf: Vec<u8>, /// A sequence of position oriented color specifications. Namely, each /// element is a position and a color spec, where the color spec should /// be applied at the position inside of `buf`. /// /// A missing color spec implies the underlying console should be reset.
colors: Vec<(usize, Option<ColorSpec>)>,
}
#[cfg(windows)] impl WindowsBuffer { /// Create a new empty buffer for Windows console coloring. fn new() -> WindowsBuffer {
WindowsBuffer { buf: vec![], colors: vec![] }
}
/// Push the given color specification into this buffer. /// /// This has the effect of setting the given color information at the /// current position in the buffer. fn push(&mutself, spec: Option<ColorSpec>) { let pos = self.buf.len(); self.colors.push((pos, spec));
}
/// Print the contents to the given stream handle, and use the console /// for coloring. fn print(
&self,
console: &mut wincon::Console,
stream: &mut LossyStandardStream<IoStandardStreamLock>,
) -> io::Result<()> { letmut last = 0; for &(pos, ref spec) in &self.colors {
stream.write_all(&self.buf[last..pos])?;
stream.flush()?;
last = pos; match *spec {
None => console.reset()?,
Some(ref spec) => spec.write_console(console)?,
}
}
stream.write_all(&self.buf[last..])?;
stream.flush()
}
impl ColorSpec { /// Create a new color specification that has no colors or styles. pubfn new() -> ColorSpec {
ColorSpec::default()
}
/// Get the foreground color. pubfn fg(&self) -> Option<&Color> { self.fg_color.as_ref()
}
/// Set the foreground color. pubfn set_fg(&mutself, color: Option<Color>) -> &mut ColorSpec { self.fg_color = color; self
}
/// Get the background color. pubfn bg(&self) -> Option<&Color> { self.bg_color.as_ref()
}
/// Set the background color. pubfn set_bg(&mutself, color: Option<Color>) -> &mut ColorSpec { self.bg_color = color; self
}
/// Get whether this is bold or not. /// /// Note that the bold setting has no effect in a Windows console. pubfn bold(&self) -> bool { self.bold
}
/// Set whether the text is bolded or not. /// /// Note that the bold setting has no effect in a Windows console. pubfn set_bold(&mutself, yes: bool) -> &mut ColorSpec { self.bold = yes; self
}
/// Get whether this is dimmed or not. /// /// Note that the dimmed setting has no effect in a Windows console. pubfn dimmed(&self) -> bool { self.dimmed
}
/// Set whether the text is dimmed or not. /// /// Note that the dimmed setting has no effect in a Windows console. pubfn set_dimmed(&mutself, yes: bool) -> &mut ColorSpec { self.dimmed = yes; self
}
/// Get whether this is italic or not. /// /// Note that the italic setting has no effect in a Windows console. pubfn italic(&self) -> bool { self.italic
}
/// Set whether the text is italicized or not. /// /// Note that the italic setting has no effect in a Windows console. pubfn set_italic(&mutself, yes: bool) -> &mut ColorSpec { self.italic = yes; self
}
/// Get whether this is underline or not. /// /// Note that the underline setting has no effect in a Windows console. pubfn underline(&self) -> bool { self.underline
}
/// Set whether the text is underlined or not. /// /// Note that the underline setting has no effect in a Windows console. pubfn set_underline(&mutself, yes: bool) -> &mut ColorSpec { self.underline = yes; self
}
/// Get whether this is strikethrough or not. /// /// Note that the strikethrough setting has no effect in a Windows console. pubfn strikethrough(&self) -> bool { self.strikethrough
}
/// Set whether the text is strikethrough or not. /// /// Note that the strikethrough setting has no effect in a Windows console. pubfn set_strikethrough(&mutself, yes: bool) -> &mut ColorSpec { self.strikethrough = yes; self
}
/// Get whether reset is enabled or not. /// /// reset is enabled by default. When disabled and using ANSI escape /// sequences, a "reset" code will be emitted every time a `ColorSpec`'s /// settings are applied. /// /// Note that the reset setting has no effect in a Windows console. pubfn reset(&self) -> bool { self.reset
}
/// Set whether to reset the terminal whenever color settings are applied. /// /// reset is enabled by default. When disabled and using ANSI escape /// sequences, a "reset" code will be emitted every time a `ColorSpec`'s /// settings are applied. /// /// Typically this is useful if callers have a requirement to more /// scrupulously manage the exact sequence of escape codes that are emitted /// when using ANSI for colors. /// /// Note that the reset setting has no effect in a Windows console. pubfn set_reset(&mutself, yes: bool) -> &mut ColorSpec { self.reset = yes; self
}
/// Get whether this is intense or not. /// /// On Unix-like systems, this will output the ANSI escape sequence /// that will print a high-intensity version of the color /// specified. /// /// On Windows systems, this will output the ANSI escape sequence /// that will print a brighter version of the color specified. pubfn intense(&self) -> bool { self.intense
}
/// Set whether the text is intense or not. /// /// On Unix-like systems, this will output the ANSI escape sequence /// that will print a high-intensity version of the color /// specified. /// /// On Windows systems, this will output the ANSI escape sequence /// that will print a brighter version of the color specified. pubfn set_intense(&mutself, yes: bool) -> &>mut ColorSpec { self.intense = yes; self
}
/// Returns true if this color specification has no colors or styles. pubfn is_none(&self) -> bool { self.fg_color.is_none()
&& self.bg_color.is_none()
&& !self.bold
&& !self.underline
&& !self.dimmed
&& !self.italic
&& !self.intense
&& !self.strikethrough
}
/// Clears this color specification so that it has no color/style settings. pubfn clear(&mutself) { self.fg_color = None; self.bg_color = None; self.bold = false; self.underline = false; self.intense = false; self.dimmed = false; self.italic = false; self.strikethrough = false;
}
/// Writes this color spec to the given Windows console. #[cfg(windows)] fn write_console(&self, console: &mut wincon::Console) -> io::Result<()> { let fg_color = self.fg_color.and_then(|c| c.to_windows(self.intense)); iflet Some((intense, color)) = fg_color {
console.fg(intense, color)?;
} let bg_color = self.bg_color.and_then(|c| c.to_windows(self.intense)); iflet Some((intense, color)) = bg_color {
console.bg(intense, color)?;
}
Ok(())
}
}
/// The set of available colors for the terminal foreground/background. /// /// The `Ansi256` and `Rgb` colors will only output the correct codes when /// paired with the `Ansi` `WriteColor` implementation. /// /// The `Ansi256` and `Rgb` color types are not supported when writing colors /// on Windows using the console. If they are used on Windows, then they are /// silently ignored and no colors will be emitted. /// /// This set may expand over time. /// /// This type has a `FromStr` impl that can parse colors from their human /// readable form. The format is as follows: /// /// 1. Any of the explicitly listed colors in English. They are matched /// case insensitively. /// 2. A single 8-bit integer, in either decimal or hexadecimal format. /// 3. A triple of 8-bit integers separated by a comma, where each integer is /// in decimal or hexadecimal format. /// /// Hexadecimal numbers are written with a `0x` prefix. #[allow(missing_docs)] #[derive(Clone, Copy, Debug, Eq, PartialEq)] pubenum Color {
Black,
Blue,
Green,
Red,
Cyan,
Magenta,
Yellow,
White,
Ansi256(u8),
Rgb(u8, u8, u8), #[doc(hidden)]
__Nonexhaustive,
}
impl Color { /// Translate this color to a wincon::Color. #[cfg(windows)] fn to_windows( self,
intense: bool,
) -> Option<(wincon::Intense, wincon::Color)> { use wincon::Intense::{No, Yes};
/// Parses a numeric color string, either ANSI or RGB. fn from_str_numeric(s: &str) -> Result<Color, ParseColorError> { // The "ansi256" format is a single number (decimal or hex) // corresponding to one of 256 colors. // // The "rgb" format is a triple of numbers (decimal or hex) delimited // by a comma corresponding to one of 256^3 colors.
fn parse_number(s: &str) -> Option<u8> { use std::u8;
fn all_attributes() -> Vec<ColorSpec> { letmut result = vec![]; for fg in vec![None, Some(Color::Red)] { for bg in vec![None, Some(Color::Red)] { for bold in vec![false, true] { for underline in vec![false, true] { for intense in vec![false, true] { for italic in vec![false, true] { for strikethrough in vec![false, true] { for dimmed in vec![false, true] { letmut color = ColorSpec::new();
color.set_fg(fg);
color.set_bg(bg);
color.set_bold(bold);
color.set_underline(underline);
color.set_intense(intense);
color.set_italic(italic);
color.set_dimmed(dimmed);
color.set_strikethrough(strikethrough);
result.push(color);
}
}
}
}
}
}
}
}
result
}
#[test] fn test_is_none() { for (i, color) in all_attributes().iter().enumerate() {
assert_eq!(
i == 0,
color.is_none(), "{:?} => {}",
color,
color.is_none()
)
}
}
#[test] fn test_clear() { for color in all_attributes() { letmut color1 = color.clone();
color1.clear();
assert!(color1.is_none(), "{:?} => {:?}", color, color1);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.