From 433dbb31bfb488caecf52c8a4695f2b9b19c6af5 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Fri, 24 May 2019 19:01:27 -0700 Subject: [PATCH] Switch from 'term' crate to 'termcolor' crate The `term` crate is unmaintained: https://github.com/Stebalien/term/issues/93 --- Cargo.lock | 33 ++++++-- Cargo.toml | 12 +-- README.md | 17 ++-- src/macros/status.rs | 16 ++-- src/shell.rs | 167 +++++++++++++------------------------- src/shell/color_config.rs | 15 +++- src/shell/component.rs | 2 +- src/shell/stream.rs | 61 -------------- src/shell/terminal.rs | 60 -------------- 9 files changed, 112 insertions(+), 271 deletions(-) delete mode 100644 src/shell/stream.rs delete mode 100644 src/shell/terminal.rs diff --git a/Cargo.lock b/Cargo.lock index 58b913ee..aa3e5a51 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7,7 +7,7 @@ dependencies = [ "abscissa_derive 0.0.2", "abscissa_generator 0.0.0", "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "canonical-path 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "canonical-path 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "gumdrop 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -20,7 +20,7 @@ dependencies = [ "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", "simplelog 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -90,7 +90,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "backtrace" -version = "0.3.20" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -154,7 +154,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "canonical-path" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -214,7 +214,7 @@ name = "failure" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -727,6 +727,14 @@ dependencies = [ "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "termcolor" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "termion" version = "1.5.2" @@ -831,6 +839,15 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "wincolor" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "zeroize" version = "0.8.0" @@ -842,7 +859,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" "checksum autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0e49efa51329a5fd37e7c79db4621af617cd4e3e5bc224939808d076077077bf" -"checksum backtrace 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)" = "45934a579eff9fd0ff637ac376a4bd134f47f8fc603f0b211d696b54d61e35f1" +"checksum backtrace 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "8ca8a0a1f68b5d04b8d006c726d16e38455fc8886c51fff7c2e6df32333378e2" "checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" "checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" @@ -850,7 +867,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6d4dc3af3ee2e12f3e5d224e5e1e3d73668abbeb69e566d361f7d5563a4fdf09" "checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" "checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" -"checksum canonical-path 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4a51f8a74c9193effe8e8a1e38ffcfcee4a0f647495d34f1b67e797c3c8a098b" +"checksum canonical-path 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e6e9e01327e6c86e92ec72b1c798d4a94810f147209bbe3ffab6a86954937a6f" "checksum cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)" = "39f75544d7bbaf57560d2168f28fd649ff9c76153874db88bdbdfd839b1a7e7d" "checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" "checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878" @@ -920,6 +937,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" "checksum tempfile 3.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7dc4738f2e68ed2855de5ac9cdbe05c9216773ecde4739b2f095002ab03a13ef" "checksum term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42" +"checksum termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4096add70612622289f2fdcdbd5086dc81c1e2675e6ae58d6c4f62a16c6d7f2f" "checksum termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dde0593aeb8d47accea5392b39350015b5eccb12c0d98044d856983d89548dea" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" @@ -935,4 +953,5 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +"checksum wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba" "checksum zeroize 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b60a6c572b91d8ecb0a460950d84fe5b40699edd07d65f73789b31237afc8f66" diff --git a/Cargo.toml b/Cargo.toml index fa7c5f3d..ea2eb944 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ edition = "2018" homepage = "https://github.com/iqlusioninc/abscissa/" repository = "https://github.com/iqlusioninc/abscissa/tree/develop/" readme = "README.md" -categories = ["command-line-interface", "rust-patterns", "web-programming"] +categories = ["command-line-interface", "rust-patterns"] keywords = ["abscissa", "cli", "application", "framework", "service"] [dependencies] @@ -30,7 +30,7 @@ secrecy = { version = "0.1", optional = true } semver = { version = "0.9", optional = true } serde = { version = "1", optional = true, features = ["serde_derive"] } simplelog = { version = "0.5", optional = true } -term = { version = "0.5", optional = true, default-features = false } +termcolor = { version = "1", optional = true } toml = { version = "0.5", optional = true } [dev-dependencies] @@ -47,7 +47,7 @@ inflector = ["heck"] logging = ["errors", "log", "simplelog"] options = ["errors", "gumdrop", "gumdrop_derive"] secrets = ["secrecy"] -shell = ["atty", "errors", "term"] +shell = ["atty", "errors", "termcolor"] status = ["shell"] time = ["chrono"] @@ -55,8 +55,4 @@ time = ["chrono"] travis-ci = { repository = "iqlusioninc/abscissa", branch = "develop" } [workspace] -members = [ - ".", - "abscissa_derive", - "abscissa_generator" -] +members = [".", "abscissa_derive", "abscissa_generator"] diff --git a/README.md b/README.md index c0d8da69..a729ce77 100644 --- a/README.md +++ b/README.md @@ -90,14 +90,13 @@ Here are all of Abscissa's transitive dependencies: | 17 | [semver-parser] | [@steveklabnik] | Apache-2.0/MIT | no† | Parser for semver spec | | 18 | [serde] | [serde-rs] | Apache-2.0/MIT | yes | Serialization framework | | 19 | [simplelog] | [@drakulix] | Apache-2.0/MIT | yes | Simple logging facility | -| 20 | [term] | [@Stebalien] | Apache-2.0/MIT | yes‡ | Terminal color support | +| 20 | [termcolor] | [@BurntSushi] | MIT/Unlicense | no | Terminal color support | | 21 | [time] | [rust-lang] | Apache-2.0/MIT | yes | Time/date library | | 22 | [toml] | [@alexcrichton] | Apache-2.0/MIT | no | TOML parser library | | 23 | [winapi]§ | [@retep998] | Apache-2.0/MIT | yes | Windows API bindings | | 24 | [zeroize] | [iqlusion] | Apache-2.0 | yes | Zero out sensitive data | * † `semver-parser` has one usage of `unsafe` which is not compiled by Abscissa. -* ‡ `term` has one usage of unsafe on Windows. Other platforms do not use unsafe. * § `winapi` also pulls in either [winapi-i686-pc-windows-gnu] or [winapi-x86_64-pc-windows-gnu] which are omitted for brevity. @@ -130,7 +129,6 @@ so you only compile the parts you need. | [abscissa_derive] | mandatory | - | [abscissa] | | [backtrace] | mandatory | - | [failure] | | [backtrace-sys] | mandatory | - | [backtrace] | -| [byteorder] | `shell` | - | [term] | | [canonical-path] | mandatory | - | [abscissa] | | [cc] | mandatory | - | [backtrace-sys], [zeroize] | | [cfg-if] | mandatory | - | [backtrace] | @@ -139,7 +137,7 @@ so you only compile the parts you need. | [failure_derive] | mandatory | - | [failure] | | [atty] | `shell` | - | [abscissa] | | [lazy_static] | mandatory | - | [abscissa] | -| [libc] | `shell` | `unix` | [atty] | +| [libc] | `shell` | `unix` | [atty] | | [log] | `logging` | - | [abscissa] | | [num-integer] | `logging` | - | [chrono] | | [num-traits] | `logging` | - | [chrono], [num-integer] | @@ -151,11 +149,11 @@ so you only compile the parts you need. | [serde] | `config` | - | [abscissa] | | [serde_derive] | `config` | - | [serde] | | [simplelog] | `logging` | - | [abscissa] | -| [term] | `shell` | - | [abscissa] | +| [termcolor] | `shell` | - | [abscissa] | | [time] | `logging` | - | [chrono] | | [unicode-xid] | mandatory | - | [proc-macro2] | | [version_check] | mandatory | - | [lazy_static] | -| [winapi]§ | `shell` | `windows` | [atty] | +| [winapi]§ | `shell` | `windows` | [atty] | | [zeroize] | mandatory | - | [abscissa] | * § `winapi` also pulls in either [winapi-i686-pc-windows-gnu] or [winapi-x86_64-pc-windows-gnu] @@ -233,10 +231,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -Parts of this code were taken from the [Cargo] project, which is -Copyright (c) 2014 The Rust Project Developers and also licensed -under the terms of the Apache License (Version 2.0). - ## Contribution Unless you explicitly state otherwise, any contribution intentionally @@ -296,7 +290,7 @@ without any additional terms or conditions. [simplelog]: https://crates.io/crates/simplelog [syn]: https://crates.io/crates/syn [synstructure]: https://crates.io/crates/ -[term]: https://crates.io/crates/term +[termcolor]: https://crates.io/crates/termcolor [time]: https://crates.io/crates/time [toml]: https://crates.io/crates/toml [unicode-xid]: https://crates.io/crates/unicode-xid @@ -317,7 +311,6 @@ without any additional terms or conditions. [@retep998]: https://github.com/retep998 [@SergioBenitez]: https://github.com/SergioBenitez [@softprops]: https://github.com/softprops -[@Stebalien]: https://github.com/Stebalien [@steveklabnik]: https://github.com/steveklabnik [@withoutboats]: https://github.com/withoutboats [chronotope]: https://github.com/chronotope/ diff --git a/src/macros/status.rs b/src/macros/status.rs index 665d6190..4fad7434 100644 --- a/src/macros/status.rs +++ b/src/macros/status.rs @@ -44,7 +44,7 @@ #[macro_export] macro_rules! status { ($stream:expr, $color:expr, $status:expr, $msg:expr) => { - $crate::status($stream, $color, $status, $msg, true); + $crate::status($stream, $color, true, $status, $msg, true).unwrap(); }; ($stream:expr, $color:expr, $status:expr, $fmt:expr, $($arg:tt)+) => { $crate::status!($stream, $color, $status, format!($fmt, $($arg)+)); @@ -55,7 +55,7 @@ macro_rules! status { #[macro_export] macro_rules! status_nojust { ($stream:expr, $color:expr, $status:expr, $msg:expr) => { - $crate::status($stream, $color, $status, $msg, false); + $crate::status($stream, $color, true, $status, $msg, false).unwrap(); }; ($stream:expr, $color:expr, $status:expr, $fmt:expr, $($arg:tt)+) => { $crate::status!($stream, $color, $status, format!($fmt, $($arg)+)); @@ -74,7 +74,7 @@ macro_rules! status_nojust { #[macro_export] macro_rules! status_ok { ($status:expr, $msg:expr) => { - $crate::status!($crate::Stream::Stdout, $crate::shell::color::GREEN, $status, $msg); + $crate::status!($crate::Stream::Stdout, $crate::shell::Color::Green, $status, $msg); }; ($status:expr, $fmt:expr, $($arg:tt)+) => { $crate::status_ok!($status, format!($fmt, $($arg)+)); @@ -93,7 +93,7 @@ macro_rules! status_ok { #[macro_export] macro_rules! status_info { ($status:expr, $msg:expr) => { - $crate::status!($crate::Stream::Stdout, $crate::shell::color::BRIGHT_CYAN, $status, $msg); + $crate::status!($crate::Stream::Stdout, $crate::shell::Color::Cyan, $status, $msg); }; ($status:expr, $fmt:expr, $($arg:tt)+) => { $crate::status_info!($status, format!($fmt, $($arg)+)); @@ -112,7 +112,7 @@ macro_rules! status_info { #[macro_export] macro_rules! status_warn { ($msg:expr) => { - $crate::status_nojust!($crate::Stream::Stdout, $crate::shell::color::YELLOW, "warning:", $msg); + $crate::status_nojust!($crate::Stream::Stdout, $crate::shell::Color::Yellow, "warning:", $msg); }; ($fmt:expr, $($arg:tt)+) => { $crate::status_warn!(format!($fmt, $($arg)+)); @@ -131,7 +131,7 @@ macro_rules! status_warn { #[macro_export] macro_rules! status_err { ($msg:expr) => { - $crate::status_nojust!($crate::Stream::Stderr, $crate::shell::color::RED, "error:", $msg); + $crate::status_nojust!($crate::Stream::Stderr, $crate::shell::Color::Red, "error:", $msg); }; ($fmt:expr, $($arg:tt)+) => { $crate::status_err!(format!($fmt, $($arg)+)); @@ -173,7 +173,7 @@ macro_rules! status_attr { #[macro_export] macro_rules! status_attr_ok { ($attr:expr, $msg:expr) => { - $crate::status_attr!($crate::Stream::Stdout, $crate::shell::color::GREEN, $attr, $msg); + $crate::status_attr!($crate::Stream::Stdout, $crate::shell::Color::Green, $attr, $msg); }; ($attr: expr, $fmt:expr, $($arg:tt)+) => { $crate::status_attr_ok!($attr, format!($fmt, $($arg)+)); @@ -192,7 +192,7 @@ macro_rules! status_attr_ok { #[macro_export] macro_rules! status_attr_err { ($attr:expr, $msg:expr) => { - $crate::status_attr!($crate::Stream::Stderr, $crate::shell::color::RED, $attr, $msg); + $crate::status_attr!($crate::Stream::Stderr, $crate::shell::Color::Red, $attr, $msg); }; ($attr: expr, $fmt:expr, $($arg:tt)+) => { $crate::status_attr_err!($attr, format!($fmt, $($arg)+)); diff --git a/src/shell.rs b/src/shell.rs index b3be1da6..8d4b65ae 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -1,137 +1,80 @@ -//! Terminal handling code -//! -//! Some portions borrowed from the Cargo project: https://github.com/rust-lang/cargo -//! -//! These portions are redistributed under the same license as Cargo +//! Shell/terminal colors and interactions mod color_config; #[cfg(feature = "application")] mod component; -mod stream; -mod terminal; +pub use self::color_config::ColorConfig; #[cfg(feature = "application")] pub use self::component::ShellComponent; -use self::terminal::Terminal; -pub use self::{color_config::ColorConfig, stream::Stream}; use crate::error::FrameworkError; -use std::{ - fmt::Display, - io::{self, Write}, -}; -pub use term::color::{self, Color}; -use term::Attr; +use std::{fmt::Display, io::Write, sync::RwLock}; +pub use termcolor::Color; +use termcolor::{ColorSpec, StandardStream, WriteColor}; +use lazy_static::lazy_static; + +lazy_static! { + /// Color configuration + static ref COLOR_CONFIG: RwLock = RwLock::new(ColorConfig::Auto); +} /// Say a status message with the given color -pub fn status(stream: Stream, color: Color, status: T, message: U, justified: bool) +pub fn status( + stream: Stream, + color: Color, + bold: bool, + status: T, + message: U, + justified: bool, +) -> Result<(), FrameworkError> where T: Display, U: Display, { - let shell = stream.lock_shell(); + let mut s = stream.open(); + s.reset()?; + s.set_color(ColorSpec::new().set_fg(Some(color)).set_bold(bold))?; + + if justified { + write!(s, "{:>12}", status)?; + } else { + write!(s, "{}", status)?; + } - shell - .borrow_mut() - .status(color, status, message, justified) - .unwrap(); + s.reset()?; + writeln!(s, " {}", message)?; + s.flush()?; + + Ok(()) } -/// Reconfigure the shell (invoke this via `ColorConfig`) +// TODO(tarcieri): hold onto shell streams in the shell component fn config(color_config: ColorConfig) { - for stream in &[Stream::Stdout, Stream::Stderr] { - let shell = stream.lock_shell(); - shell.replace(Shell::new(*stream, color_config)); - } + let mut global_config = COLOR_CONFIG.write().unwrap(); + *global_config = color_config; } -/// Shell we output to (either STDOUT or STDERR) -pub(crate) struct Shell(Terminal); - -impl Shell { - /// Create a new shell for the given stream - pub(crate) fn new(stream: Stream, color_config: ColorConfig) -> Self { - let terminal = Terminal::new(stream.writer(), color_config, stream.is_tty()) - .unwrap_or_else(|_| Terminal::from(stream.writer())); - - Shell(terminal) - } - - /// Say a status message with the given color - pub(crate) fn status( - &mut self, - clr: Color, - status: T, - message: U, - justified: bool, - ) -> Result<(), FrameworkError> - where - T: Display, - U: Display, - { - self.reset()?; - - if clr != color::BLACK { - self.fg(clr)?; - } - - if self.supports_attr(Attr::Bold) { - self.attr(Attr::Bold)?; - } - - if justified { - write!(self, "{:>12}", status.to_string())?; - } else { - write!(self, "{}", status)?; - } - - self.reset()?; - writeln!(self, " {}", message)?; - self.flush()?; - - Ok(()) - } - - fn fg(&mut self, clr: Color) -> Result { - if let Terminal::Colored(ref mut c) = self.0 { - c.fg(clr)?; - Ok(true) - } else { - Ok(false) - } - } - - fn attr(&mut self, attr: Attr) -> Result { - if let Terminal::Colored(ref mut c) = self.0 { - c.attr(attr)?; - Ok(true) - } else { - Ok(false) - } - } - - fn supports_attr(&self, attr: Attr) -> bool { - if let Terminal::Colored(ref c) = self.0 { - c.supports_attr(attr) - } else { - false - } - } - - fn reset(&mut self) -> Result<(), FrameworkError> { - if let Terminal::Colored(ref mut c) = self.0 { - c.reset()?; - } +/// Terminal streams +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum Stream { + /// Standard output + Stdout, - Ok(()) - } + /// Standard error + Stderr, } -impl Write for Shell { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - - fn flush(&mut self) -> io::Result<()> { - self.0.flush() +impl Stream { + /// Open the given stream + fn open(self) -> StandardStream { + let color_choice = { + let cfg = COLOR_CONFIG.read().unwrap(); + *cfg + }.into(); + + match self { + Stream::Stdout => StandardStream::stdout(color_choice), + Stream::Stderr => StandardStream::stderr(color_choice), + } } } diff --git a/src/shell/color_config.rs b/src/shell/color_config.rs index d90e6a6b..1226f60f 100644 --- a/src/shell/color_config.rs +++ b/src/shell/color_config.rs @@ -1,14 +1,15 @@ use crate::error::{FrameworkError, FrameworkErrorKind::ParseError}; -#[cfg(feature = "serde_derive")] +#[cfg(feature = "serde")] use serde::Deserialize; use std::{ fmt::{self, Display}, str::FromStr, }; +use termcolor::ColorChoice; /// Color configuration #[derive(Clone, Copy, Debug, Eq, PartialEq)] -#[cfg_attr(feature = "serde_derive", derive(Deserialize))] +#[cfg_attr(feature = "serde", derive(Deserialize))] pub enum ColorConfig { /// Pick colors automatically based on whether we're using a TTY Auto, @@ -56,3 +57,13 @@ impl ColorConfig { super::config(self) } } + +impl From for ColorChoice { + fn from(config: ColorConfig) -> ColorChoice { + match config { + ColorConfig::Auto => ColorChoice::Auto, + ColorConfig::Always => ColorChoice::Always, + ColorConfig::Never => ColorChoice::Never, + } + } +} diff --git a/src/shell/component.rs b/src/shell/component.rs index 32874b8b..84fcdfdc 100644 --- a/src/shell/component.rs +++ b/src/shell/component.rs @@ -28,7 +28,7 @@ where /// Initialize this component at the time the framework boots fn after_config(&mut self, _app: Option<&A::Cfg>) -> Result<(), FrameworkError> { - super::config(self.0); + // TODO(tarcieri): actually configure Ok(()) } } diff --git a/src/shell/stream.rs b/src/shell/stream.rs deleted file mode 100644 index ef507268..00000000 --- a/src/shell/stream.rs +++ /dev/null @@ -1,61 +0,0 @@ -//! Access to standard output and standard error - -use super::{ColorConfig, Shell}; -use lazy_static::lazy_static; -use std::{ - cell::RefCell, - io::{self, Write}, - sync::{Mutex, MutexGuard}, -}; - -lazy_static! { - static ref STDOUT: Mutex> = { - Mutex::new(RefCell::new(Shell::new( - Stream::Stdout, - ColorConfig::default(), - ))) - }; - static ref STDERR: Mutex> = { - Mutex::new(RefCell::new(Shell::new( - Stream::Stderr, - ColorConfig::default(), - ))) - }; -} - -/// Terminal streams -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum Stream { - /// Standard output - Stdout, - - /// Standard error - Stderr, -} - -impl Stream { - /// Get a shell for this stream type - pub(crate) fn lock_shell(&self) -> MutexGuard<'_, RefCell> { - match self { - // TODO: better handle `PoisonError`? - Stream::Stdout => STDOUT.lock().unwrap(), - Stream::Stderr => STDERR.lock().unwrap(), - } - } - - /// Get a boxed writer for this stream - pub(crate) fn writer(self) -> Box { - match self { - Stream::Stdout => Box::new(io::stdout()), - Stream::Stderr => Box::new(io::stderr()), - } - } - - /// Is this stream a TTY? - pub fn is_tty(self) -> bool { - atty::is(match self { - Stream::Stdout => atty::Stream::Stdout, - Stream::Stderr => atty::Stream::Stderr, - }) - } -} diff --git a/src/shell/terminal.rs b/src/shell/terminal.rs deleted file mode 100644 index 968914e9..00000000 --- a/src/shell/terminal.rs +++ /dev/null @@ -1,60 +0,0 @@ -use super::color_config::ColorConfig; -use crate::error::FrameworkError; -use std::io::{self, Write}; -use term::{self, terminfo::TermInfo, Terminal as TerminalTrait, TerminfoTerminal}; - -/// Terminal I/O object -pub(super) enum Terminal { - NoColor(Box), - Colored(Box> + Send>), -} - -impl Terminal { - /// Create a new shell for the given stream - #[allow(clippy::new_ret_no_self)] - pub(super) fn new( - writer: Box, - color_config: ColorConfig, - is_tty: bool, - ) -> Result { - let terminfo = TermInfo::from_env()?; - let terminfo_terminal = TerminfoTerminal::new_with_terminfo(writer, terminfo); - - let terminal = match color_config { - ColorConfig::Always => Terminal::Colored(Box::new(terminfo_terminal)), - ColorConfig::Auto => { - if is_tty && terminfo_terminal.supports_color() { - Terminal::Colored(Box::new(terminfo_terminal)) - } else { - Terminal::NoColor(terminfo_terminal.into_inner()) - } - } - ColorConfig::Never => Terminal::NoColor(terminfo_terminal.into_inner()), - }; - - Ok(terminal) - } -} - -impl From> for Terminal { - /// Create a Terminal with no color configuration - fn from(writer: Box) -> Self { - Terminal::NoColor(writer) - } -} - -impl Write for Terminal { - fn write(&mut self, buf: &[u8]) -> io::Result { - match self { - Terminal::Colored(ref mut c) => c.write(buf), - Terminal::NoColor(ref mut n) => n.write(buf), - } - } - - fn flush(&mut self) -> io::Result<()> { - match self { - Terminal::Colored(ref mut c) => c.flush(), - Terminal::NoColor(ref mut n) => n.flush(), - } - } -}