diff --git a/Cargo.lock b/Cargo.lock index 0201e7c9cc5..84db9a1ad3e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,9 +25,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.5.0" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c" +checksum = "83d7b3983a025adeb201ef26a5564ebd1641ea9851f6282aee4940f745a3c07c" dependencies = [ "anstyle", "anstyle-parse", @@ -39,9 +39,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b84bf0a05bbb2a83e5eb6fa36bb6e87baa08193c35ff52bbf6b38d8af2890e46" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anstyle-parse" @@ -61,21 +61,11 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "anstyle-termcolor" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11c3d1411f1f4c8a7b177caec3c71b51290f9e8ad9f99124fd3fe9aa96e56834" -dependencies = [ - "anstyle", - "termcolor", -] - [[package]] name = "anstyle-wincon" -version = "2.1.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" dependencies = [ "anstyle", "windows-sys", @@ -93,12 +83,6 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - [[package]] name = "autocfg" version = "1.1.0" @@ -252,8 +236,8 @@ dependencies = [ name = "cargo" version = "0.75.0" dependencies = [ + "anstream", "anstyle", - "anstyle-termcolor", "anyhow", "base64", "bytesize", @@ -272,7 +256,6 @@ dependencies = [ "curl-sys", "filetime", "flate2", - "fwdansi", "git2", "git2-curl", "gix", @@ -310,11 +293,9 @@ dependencies = [ "sha1", "shell-escape", "snapbox", - "strip-ansi-escapes", "syn 2.0.29", "tar", "tempfile", - "termcolor", "time", "toml", "toml_edit", @@ -400,6 +381,8 @@ version = "0.1.0" name = "cargo-test-support" version = "0.1.0" dependencies = [ + "anstream", + "anstyle", "anyhow", "cargo-test-macro", "cargo-util", @@ -414,7 +397,6 @@ dependencies = [ "serde_json", "snapbox", "tar", - "termcolor", "time", "toml", "url", @@ -505,18 +487,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.4" +version = "4.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d7b8d5ec32af0fadc644bf1fd509a688c2103b185644bb1e29d164e0703136" +checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.4.4" +version = "4.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5179bb514e4d7c2051749d8fcefa2ed6d06a9f4e6d69faf3805f5d80b8cf8d56" +checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45" dependencies = [ "anstream", "anstyle", @@ -998,16 +980,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "fwdansi" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08c1f5787fe85505d1f7777268db5103d80a7a374d2316a7ce262e57baf8f208" -dependencies = [ - "memchr", - "termcolor", -] - [[package]] name = "generic-array" version = "0.14.7" @@ -3071,9 +3043,9 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "snapbox" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad90eb3a2e3a8031d636d45bd4832751aefd58a291b553f7305a2bacae21aff3" +checksum = "7b439536a42c43be148b610c7f7f968fb79a457254910a9cb20900da73cd3271" dependencies = [ "anstream", "anstyle", @@ -3090,9 +3062,9 @@ dependencies = [ [[package]] name = "snapbox-macros" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95f4ffd811b87da98d0e48285134b7847954bd76e843bb794a893b47ca3ee325" +checksum = "ed1559baff8a696add3322b9be3e940d433e7bb4e38d79017205fd37ff28b28e" dependencies = [ "anstream", ] @@ -3117,15 +3089,6 @@ dependencies = [ "der", ] -[[package]] -name = "strip-ansi-escapes" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "011cbb39cf7c1f62871aea3cc46e5817b0937b49e9447370c93cacbe93a766d8" -dependencies = [ - "vte", -] - [[package]] name = "strsim" version = "0.10.0" @@ -3195,15 +3158,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "termcolor" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" -dependencies = [ - "winapi-util", -] - [[package]] name = "terminal_size" version = "0.3.0" @@ -3583,27 +3537,6 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" -[[package]] -name = "vte" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cbce692ab4ca2f1f3047fcf732430249c0e971bfdd2b234cf2c47ad93af5983" -dependencies = [ - "arrayvec", - "utf8parse", - "vte_generate_state_changes", -] - -[[package]] -name = "vte_generate_state_changes" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff" -dependencies = [ - "proc-macro2", - "quote", -] - [[package]] name = "wait-timeout" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index c361c4a191f..42e68e95c16 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,8 +16,8 @@ edition = "2021" license = "MIT OR Apache-2.0" [workspace.dependencies] -anstyle = "1.0.3" -anstyle-termcolor = "1.1.0" +anstream = "0.6.3" +anstyle = "1.0.4" anyhow = "1.0.75" base64 = "0.21.3" bytesize = "1.3" @@ -31,7 +31,7 @@ cargo-test-macro = { path = "crates/cargo-test-macro" } cargo-test-support = { path = "crates/cargo-test-support" } cargo-util = { version = "0.2.6", path = "crates/cargo-util" } cargo_metadata = "0.17.0" -clap = "4.4.4" +clap = "4.4.6" color-print = "0.3.4" core-foundation = { version = "0.9.3", features = ["mac_os_10_7_support"] } crates-io = { version = "0.39.0", path = "crates/crates-io" } @@ -40,7 +40,6 @@ curl = "0.4.44" curl-sys = "0.4.66" filetime = "0.2.22" flate2 = { version = "1.0.27", default-features = false, features = ["zlib"] } -fwdansi = "1.1.0" git2 = "0.18.0" git2-curl = "0.19.0" gix = { version = "0.54.1", default-features = false, features = ["blocking-http-transport-curl", "progress-tree", "revision"] } @@ -86,12 +85,10 @@ serde_json = "1.0.105" sha1 = "0.10.5" sha2 = "0.10.7" shell-escape = "0.1.5" -snapbox = { version = "0.4.12", features = ["diff", "path"] } -strip-ansi-escapes = "0.1.1" +snapbox = { version = "0.4.13", features = ["diff", "path"] } syn = { version = "2.0.29", features = ["extra-traits", "full"] } tar = { version = "0.4.40", default-features = false } tempfile = "3.8.0" -termcolor = "1.2.0" thiserror = "1.0.47" time = { version = "0.3", features = ["parsing", "formatting", "serde"] } toml = "0.7.6" @@ -123,8 +120,8 @@ name = "cargo" path = "src/cargo/lib.rs" [dependencies] +anstream.workspace = true anstyle.workspace = true -anstyle-termcolor.workspace = true anyhow.workspace = true base64.workspace = true bytesize.workspace = true @@ -175,11 +172,9 @@ serde_ignored.workspace = true serde_json = { workspace = true, features = ["raw_value"] } sha1.workspace = true shell-escape.workspace = true -strip-ansi-escapes.workspace = true syn.workspace = true tar.workspace = true tempfile.workspace = true -termcolor.workspace = true time.workspace = true toml.workspace = true toml_edit.workspace = true @@ -194,9 +189,6 @@ walkdir.workspace = true [target.'cfg(not(windows))'.dependencies] openssl = { workspace = true, optional = true } -[target.'cfg(windows)'.dependencies] -fwdansi.workspace = true - [target.'cfg(windows)'.dependencies.windows-sys] workspace = true features = [ diff --git a/crates/cargo-test-support/Cargo.toml b/crates/cargo-test-support/Cargo.toml index cfcf139de21..fc32e1c9cba 100644 --- a/crates/cargo-test-support/Cargo.toml +++ b/crates/cargo-test-support/Cargo.toml @@ -10,6 +10,8 @@ publish = false doctest = false [dependencies] +anstream.workspace = true +anstyle.workspace = true anyhow.workspace = true cargo-test-macro.workspace = true cargo-util.workspace = true @@ -24,7 +26,6 @@ serde = { workspace = true, features = ["derive"] } serde_json.workspace = true snapbox.workspace = true tar.workspace = true -termcolor.workspace = true time.workspace = true toml.workspace = true url.workspace = true diff --git a/crates/cargo-test-support/src/diff.rs b/crates/cargo-test-support/src/diff.rs index f3b283b109f..3fedc839bf1 100644 --- a/crates/cargo-test-support/src/diff.rs +++ b/crates/cargo-test-support/src/diff.rs @@ -7,7 +7,6 @@ use std::fmt; use std::io::Write; -use termcolor::{Ansi, Color, ColorSpec, NoColor, WriteColor}; /// A single line change to be applied to the original. #[derive(Debug, Eq, PartialEq)] @@ -111,42 +110,35 @@ where } pub fn render_colored_changes(changes: &[Change]) -> String { - // termcolor is not very ergonomic, but I don't want to bring in another dependency. - let mut red = ColorSpec::new(); - red.set_fg(Some(Color::Red)); - let mut green = ColorSpec::new(); - green.set_fg(Some(Color::Green)); - let mut dim = ColorSpec::new(); - dim.set_dimmed(true); - let mut v = Vec::new(); - let mut result: Box = if crate::is_ci() { + // anstyle is not very ergonomic, but I don't want to bring in another dependency. + let red = anstyle::AnsiColor::Red.on_default().render(); + let green = anstyle::AnsiColor::Green.on_default().render(); + let dim = (anstyle::Style::new() | anstyle::Effects::DIMMED).render(); + let bold = (anstyle::Style::new() | anstyle::Effects::BOLD).render(); + let reset = anstyle::Reset.render(); + + let choice = if crate::is_ci() { // Don't use color on CI. Even though GitHub can display colors, it // makes reading the raw logs more difficult. - Box::new(NoColor::new(&mut v)) + anstream::ColorChoice::Never } else { - Box::new(Ansi::new(&mut v)) + anstream::AutoStream::choice(&std::io::stdout()) }; + let mut buffer = anstream::AutoStream::new(Vec::new(), choice); for change in changes { let (nums, sign, color, text) = match change { - Change::Add(i, s) => (format!(" {:<4} ", i), '+', &green, s), - Change::Remove(i, s) => (format!("{:<4} ", i), '-', &red, s), - Change::Keep(x, y, s) => (format!("{:<4}{:<4} ", x, y), ' ', &dim, s), + Change::Add(i, s) => (format!(" {:<4} ", i), '+', green, s), + Change::Remove(i, s) => (format!("{:<4} ", i), '-', red, s), + Change::Keep(x, y, s) => (format!("{:<4}{:<4} ", x, y), ' ', dim, s), }; - result.set_color(&dim).unwrap(); - write!(result, "{}", nums).unwrap(); - let mut bold = color.clone(); - bold.set_bold(true); - result.set_color(&bold).unwrap(); - write!(result, "{}", sign).unwrap(); - result.reset().unwrap(); - result.set_color(&color).unwrap(); - write!(result, "{}", text).unwrap(); - result.reset().unwrap(); - writeln!(result).unwrap(); + write!( + buffer, + "{dim}{nums}{reset}{bold}{sign}{reset}{color}{text}{reset}" + ) + .unwrap(); } - drop(result); - String::from_utf8(v).unwrap() + String::from_utf8(buffer.into_inner()).unwrap() } #[cfg(test)] diff --git a/src/bin/cargo/commands/report.rs b/src/bin/cargo/commands/report.rs index adc4dcb9220..bc00d5bb830 100644 --- a/src/bin/cargo/commands/report.rs +++ b/src/bin/cargo/commands/report.rs @@ -44,7 +44,7 @@ fn report_future_incompatibilities(config: &Config, args: &ArgMatches) -> CliRes .value_of_u32("id")? .unwrap_or_else(|| reports.last_id()); let krate = args.get_one::("package").map(String::as_str); - let report = reports.get_report(id, config, krate)?; + let report = reports.get_report(id, krate)?; drop_println!(config, "{}", REPORT_PREAMBLE); drop(config.shell().print_ansi_stdout(report.as_bytes())); Ok(()) diff --git a/src/cargo/core/compiler/future_incompat.rs b/src/cargo/core/compiler/future_incompat.rs index cfc028cf00a..907a0f97d63 100644 --- a/src/cargo/core/compiler/future_incompat.rs +++ b/src/cargo/core/compiler/future_incompat.rs @@ -37,7 +37,7 @@ use crate::core::compiler::BuildContext; use crate::core::{Dependency, PackageId, Workspace}; use crate::sources::source::QueryKind; use crate::sources::SourceConfigMap; -use crate::util::{iter_join, CargoResult, Config}; +use crate::util::{iter_join, CargoResult}; use anyhow::{bail, format_err, Context}; use serde::{Deserialize, Serialize}; use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; @@ -224,12 +224,8 @@ impl OnDiskReports { self.reports.last().map(|r| r.id).unwrap() } - pub fn get_report( - &self, - id: u32, - config: &Config, - package: Option<&str>, - ) -> CargoResult { + /// Returns an ANSI-styled report + pub fn get_report(&self, id: u32, package: Option<&str>) -> CargoResult { let report = self.reports.iter().find(|r| r.id == id).ok_or_else(|| { let available = iter_join(self.reports.iter().map(|r| r.id.to_string()), ", "); format_err!( @@ -267,15 +263,6 @@ impl OnDiskReports { }; to_display += &package_report; - let shell = config.shell(); - - let to_display = if shell.err_supports_color() && shell.out_supports_color() { - to_display - } else { - strip_ansi_escapes::strip(&to_display) - .map(|v| String::from_utf8(v).expect("utf8")) - .expect("strip should never fail") - }; Ok(to_display) } } diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs index 5da3a908ed8..b0f15bd6107 100644 --- a/src/cargo/core/compiler/mod.rs +++ b/src/cargo/core/compiler/mod.rs @@ -202,7 +202,6 @@ fn compile<'cfg>( &unit.target, cx.files().message_cache_path(unit), cx.bcx.build_config.message_format, - cx.bcx.config.shell().err_supports_color(), unit.show_warnings(bcx.config), ); // Need to link targets on both the dirty and fresh. @@ -1416,8 +1415,6 @@ fn envify(s: &str) -> String { struct OutputOptions { /// What format we're emitting from Cargo itself. format: MessageFormat, - /// Whether or not to display messages in color. - color: bool, /// Where to write the JSON messages to support playback later if the unit /// is fresh. The file is created lazily so that in the normal case, lots /// of empty files are not created. If this is None, the output will not @@ -1439,14 +1436,12 @@ struct OutputOptions { impl OutputOptions { fn new(cx: &Context<'_, '_>, unit: &Unit) -> OutputOptions { - let color = cx.bcx.config.shell().err_supports_color(); let path = cx.files().message_cache_path(unit); // Remove old cache, ignore ENOENT, which is the common case. drop(fs::remove_file(&path)); let cache_cell = Some((path, LazyCell::new())); OutputOptions { format: cx.bcx.build_config.message_format, - color, cache_cell, show_diagnostics: true, warnings_seen: 0, @@ -1586,15 +1581,7 @@ fn on_stderr_line_inner( if msg.rendered.ends_with('\n') { msg.rendered.pop(); } - let rendered = if options.color { - msg.rendered - } else { - // Strip only fails if the Writer fails, which is Cursor - // on a Vec, which should never fail. - strip_ansi_escapes::strip(&msg.rendered) - .map(|v| String::from_utf8(v).expect("utf8")) - .expect("strip should never fail") - }; + let rendered = msg.rendered; if options.show_diagnostics { let machine_applicable: bool = msg .children @@ -1625,9 +1612,7 @@ fn on_stderr_line_inner( other: std::collections::BTreeMap, } if let Ok(mut error) = serde_json::from_str::(compiler_message.get()) { - error.rendered = strip_ansi_escapes::strip(&error.rendered) - .map(|v| String::from_utf8(v).expect("utf8")) - .unwrap_or(error.rendered); + error.rendered = anstream::adapter::strip_str(&error.rendered).to_string(); let new_line = serde_json::to_string(&error)?; let new_msg: Box = serde_json::from_str(&new_line)?; compiler_message = new_msg; @@ -1699,13 +1684,11 @@ fn replay_output_cache( target: &Target, path: PathBuf, format: MessageFormat, - color: bool, show_diagnostics: bool, ) -> Work { let target = target.clone(); let mut options = OutputOptions { format, - color, cache_cell: None, show_diagnostics, warnings_seen: 0, diff --git a/src/cargo/core/shell.rs b/src/cargo/core/shell.rs index 375dc9b82ff..a9b9e84afcd 100644 --- a/src/cargo/core/shell.rs +++ b/src/cargo/core/shell.rs @@ -2,9 +2,8 @@ use std::fmt; use std::io::prelude::*; use std::io::IsTerminal; +use anstream::AutoStream; use anstyle::Style; -use anstyle_termcolor::to_termcolor_spec; -use termcolor::{self, BufferWriter, StandardStream, WriteColor}; use crate::util::errors::CargoResult; use crate::util::style::*; @@ -79,17 +78,11 @@ impl fmt::Debug for Shell { /// A `Write`able object, either with or without color support enum ShellOut { /// A plain write object without color support - Write(Box), + Write(AutoStream>), /// Color-enabled stdio, with information on whether color should be used - /// - /// The separate buffered fields are used for buffered writing to the - /// corresponding stream. The non-buffered fields should be used when you - /// do not want content to be buffered. Stream { - stdout: StandardStream, - buffered_stdout: BufferWriter, - stderr: StandardStream, - buffered_stderr: BufferWriter, + stdout: AutoStream, + stderr: AutoStream, stderr_tty: bool, color_choice: ColorChoice, }, @@ -111,15 +104,13 @@ impl Shell { /// output. pub fn new() -> Shell { let auto_clr = ColorChoice::CargoAuto; - let stdout_choice = auto_clr.to_termcolor_color_choice(Stream::Stdout); - let stderr_choice = auto_clr.to_termcolor_color_choice(Stream::Stderr); + let stdout_choice = auto_clr.to_anstream_color_choice(); + let stderr_choice = auto_clr.to_anstream_color_choice(); Shell { output: ShellOut::Stream { - stdout: StandardStream::stdout(stdout_choice), - buffered_stdout: BufferWriter::stdout(stdout_choice), - stderr: StandardStream::stderr(stderr_choice), - buffered_stderr: BufferWriter::stderr(stderr_choice), - color_choice: ColorChoice::CargoAuto, + stdout: AutoStream::new(std::io::stdout(), stdout_choice), + stderr: AutoStream::new(std::io::stderr(), stderr_choice), + color_choice: auto_clr, stderr_tty: std::io::stderr().is_terminal(), }, verbosity: Verbosity::Verbose, @@ -130,7 +121,7 @@ impl Shell { /// Creates a shell from a plain writable object, with no color, and max verbosity. pub fn from_write(out: Box) -> Shell { Shell { - output: ShellOut::Write(out), + output: ShellOut::Write(AutoStream::never(out)), // strip all formatting on write verbosity: Verbosity::Verbose, needs_clear: false, } @@ -297,9 +288,7 @@ impl Shell { pub fn set_color_choice(&mut self, color: Option<&str>) -> CargoResult<()> { if let ShellOut::Stream { ref mut stdout, - ref mut buffered_stdout, ref mut stderr, - ref mut buffered_stderr, ref mut color_choice, .. } = self.output @@ -317,12 +306,10 @@ impl Shell { ), }; *color_choice = cfg; - let stdout_choice = cfg.to_termcolor_color_choice(Stream::Stdout); - let stderr_choice = cfg.to_termcolor_color_choice(Stream::Stderr); - *stdout = StandardStream::stdout(stdout_choice); - *buffered_stdout = BufferWriter::stdout(stdout_choice); - *stderr = StandardStream::stderr(stderr_choice); - *buffered_stderr = BufferWriter::stderr(stderr_choice); + let stdout_choice = cfg.to_anstream_color_choice(); + let stderr_choice = cfg.to_anstream_color_choice(); + *stdout = AutoStream::new(std::io::stdout(), stdout_choice); + *stderr = AutoStream::new(std::io::stderr(), stderr_choice); } Ok(()) } @@ -342,14 +329,14 @@ impl Shell { pub fn err_supports_color(&self) -> bool { match &self.output { ShellOut::Write(_) => false, - ShellOut::Stream { stderr, .. } => stderr.supports_color(), + ShellOut::Stream { stderr, .. } => supports_color(stderr.current_choice()), } } pub fn out_supports_color(&self) -> bool { match &self.output { ShellOut::Write(_) => false, - ShellOut::Stream { stdout, .. } => stdout.supports_color(), + ShellOut::Stream { stdout, .. } => supports_color(stdout.current_choice()), } } @@ -372,13 +359,6 @@ impl Shell { if self.needs_clear { self.err_erase_line(); } - #[cfg(windows)] - { - if let ShellOut::Stream { stderr, .. } = &mut self.output { - ::fwdansi::write_ansi(stderr, message)?; - return Ok(()); - } - } self.err().write_all(message)?; Ok(()) } @@ -388,13 +368,6 @@ impl Shell { if self.needs_clear { self.err_erase_line(); } - #[cfg(windows)] - { - if let ShellOut::Stream { stdout, .. } = &mut self.output { - ::fwdansi::write_ansi(stdout, message)?; - return Ok(()); - } - } self.out().write_all(message)?; Ok(()) } @@ -425,82 +398,43 @@ impl ShellOut { style: &Style, justified: bool, ) -> CargoResult<()> { - match *self { - ShellOut::Stream { - ref mut buffered_stderr, - .. - } => { - let mut buffer = buffered_stderr.buffer(); - buffer.reset()?; - buffer.set_color(&to_termcolor_spec(*style))?; - if justified { - write!(buffer, "{:>12}", status)?; - } else { - write!(buffer, "{}", status)?; - buffer.set_color(termcolor::ColorSpec::new().set_bold(true))?; - write!(buffer, ":")?; - } - buffer.reset()?; - match message { - Some(message) => writeln!(buffer, " {}", message)?, - None => write!(buffer, " ")?, - } - buffered_stderr.print(&buffer)?; - } - ShellOut::Write(ref mut w) => { - if justified { - write!(w, "{:>12}", status)?; - } else { - write!(w, "{}:", status)?; - } - match message { - Some(message) => writeln!(w, " {}", message)?, - None => write!(w, " ")?, - } - } + let style = style.render(); + let bold = (anstyle::Style::new() | anstyle::Effects::BOLD).render(); + let reset = anstyle::Reset.render(); + + let mut buffer = Vec::new(); + if justified { + write!(&mut buffer, "{style}{status:>12}{reset}")?; + } else { + write!(&mut buffer, "{style}{status}{reset}{bold}:{reset}")?; } + match message { + Some(message) => writeln!(buffer, " {message}")?, + None => write!(buffer, " ")?, + } + self.stderr().write_all(&buffer)?; Ok(()) } /// Write a styled fragment - fn write_stdout(&mut self, fragment: impl fmt::Display, color: &Style) -> CargoResult<()> { - match *self { - ShellOut::Stream { - ref mut buffered_stdout, - .. - } => { - let mut buffer = buffered_stdout.buffer(); - buffer.reset()?; - buffer.set_color(&to_termcolor_spec(*color))?; - write!(buffer, "{}", fragment)?; - buffer.reset()?; - buffered_stdout.print(&buffer)?; - } - ShellOut::Write(ref mut w) => { - write!(w, "{}", fragment)?; - } - } + fn write_stdout(&mut self, fragment: impl fmt::Display, style: &Style) -> CargoResult<()> { + let style = style.render(); + let reset = anstyle::Reset.render(); + + let mut buffer = Vec::new(); + write!(buffer, "{style}{}{reset}", fragment)?; + self.stdout().write_all(&buffer)?; Ok(()) } /// Write a styled fragment - fn write_stderr(&mut self, fragment: impl fmt::Display, color: &Style) -> CargoResult<()> { - match *self { - ShellOut::Stream { - ref mut buffered_stderr, - .. - } => { - let mut buffer = buffered_stderr.buffer(); - buffer.reset()?; - buffer.set_color(&to_termcolor_spec(*color))?; - write!(buffer, "{}", fragment)?; - buffer.reset()?; - buffered_stderr.print(&buffer)?; - } - ShellOut::Write(ref mut w) => { - write!(w, "{}", fragment)?; - } - } + fn write_stderr(&mut self, fragment: impl fmt::Display, style: &Style) -> CargoResult<()> { + let style = style.render(); + let reset = anstyle::Reset.render(); + + let mut buffer = Vec::new(); + write!(buffer, "{style}{}{reset}", fragment)?; + self.stderr().write_all(&buffer)?; Ok(()) } @@ -522,33 +456,22 @@ impl ShellOut { } impl ColorChoice { - /// Converts our color choice to termcolor's version. - fn to_termcolor_color_choice(self, stream: Stream) -> termcolor::ColorChoice { + /// Converts our color choice to anstream's version. + fn to_anstream_color_choice(self) -> anstream::ColorChoice { match self { - ColorChoice::Always => termcolor::ColorChoice::Always, - ColorChoice::Never => termcolor::ColorChoice::Never, - ColorChoice::CargoAuto => { - if stream.is_terminal() { - termcolor::ColorChoice::Auto - } else { - termcolor::ColorChoice::Never - } - } + ColorChoice::Always => anstream::ColorChoice::Always, + ColorChoice::Never => anstream::ColorChoice::Never, + ColorChoice::CargoAuto => anstream::ColorChoice::Auto, } } } -enum Stream { - Stdout, - Stderr, -} - -impl Stream { - fn is_terminal(self) -> bool { - match self { - Self::Stdout => std::io::stdout().is_terminal(), - Self::Stderr => std::io::stderr().is_terminal(), - } +fn supports_color(choice: anstream::ColorChoice) -> bool { + match choice { + anstream::ColorChoice::Always + | anstream::ColorChoice::AlwaysAnsi + | anstream::ColorChoice::Auto => true, + anstream::ColorChoice::Never => false, } }