From ea43692918c8742a982eaf0701c751661a263890 Mon Sep 17 00:00:00 2001 From: Shane Celis Date: Thu, 8 Jun 2023 05:54:14 -0400 Subject: [PATCH 01/11] feature: Make usable without atty. --- Cargo.toml | 4 +++- src/control.rs | 11 +++++++++-- src/lib.rs | 1 + 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 170c690..cdd3062 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,11 +11,13 @@ readme = "README.md" keywords = ["color", "string", "term", "ansi_term", "term-painter"] [features] +default = ["tty"] +tty = ["dep:atty"] # with this feature, no color will ever be written no-color = [] [dependencies] -atty = "0.2" +atty = { version = "0.2", optional = true } lazy_static = "1" [target.'cfg(windows)'.dependencies.winapi] diff --git a/src/control.rs b/src/control.rs index 7ad6e62..c229445 100644 --- a/src/control.rs +++ b/src/control.rs @@ -103,9 +103,16 @@ impl ShouldColorize { /// `CLICOLOR_FORCE` takes highest priority, followed by `NO_COLOR`, /// followed by `CLICOLOR` combined with tty check. pub fn from_env() -> Self { + #[allow(unused_mut)] + let mut tty: Option = None; + + #[cfg(feature = "tty")] + { + tty = Some(atty::is(atty::Stream::Stdout)); + } ShouldColorize { - clicolor: ShouldColorize::normalize_env(env::var("CLICOLOR")).unwrap_or_else(|| true) - && atty::is(atty::Stream::Stdout), + clicolor: ShouldColorize::normalize_env(env::var("CLICOLOR")).unwrap_or(true) + && tty.unwrap_or(false), clicolor_force: ShouldColorize::resolve_clicolor_force( env::var("NO_COLOR"), env::var("CLICOLOR_FORCE"), diff --git a/src/lib.rs b/src/lib.rs index 1158c2a..308992e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,6 +25,7 @@ //! #![warn(missing_docs)] +#[cfg(feature = "tty")] extern crate atty; #[macro_use] extern crate lazy_static; From b9c0b1e58ae09668b1f49a8c103985c9c50c7388 Mon Sep 17 00:00:00 2001 From: Shane Celis Date: Thu, 8 Jun 2023 06:49:08 -0400 Subject: [PATCH 02/11] refactor: Clean up conditional atty code. --- src/control.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/control.rs b/src/control.rs index c229445..5177a56 100644 --- a/src/control.rs +++ b/src/control.rs @@ -103,16 +103,15 @@ impl ShouldColorize { /// `CLICOLOR_FORCE` takes highest priority, followed by `NO_COLOR`, /// followed by `CLICOLOR` combined with tty check. pub fn from_env() -> Self { - #[allow(unused_mut)] - let mut tty: Option = None; + let tty: bool = if cfg!(feature = "tty") { + atty::is(atty::Stream::Stdout) + } else { + false + }; - #[cfg(feature = "tty")] - { - tty = Some(atty::is(atty::Stream::Stdout)); - } ShouldColorize { clicolor: ShouldColorize::normalize_env(env::var("CLICOLOR")).unwrap_or(true) - && tty.unwrap_or(false), + && tty, clicolor_force: ShouldColorize::resolve_clicolor_force( env::var("NO_COLOR"), env::var("CLICOLOR_FORCE"), From efd631d63da7a8174b2620a6c5f7f9903dd117db Mon Sep 17 00:00:00 2001 From: Shane Celis Date: Thu, 8 Jun 2023 06:49:44 -0400 Subject: [PATCH 03/11] feature: Use a static cow. --- src/lib.rs | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 308992e..27bb561 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -53,7 +53,7 @@ pub use style::{Style, Styles}; /// A string that may have color and/or style applied to it. #[derive(Clone, Debug, PartialEq, Eq)] pub struct ColoredString { - input: String, + input: Cow<'static, str>, fgcolor: Option, bgcolor: Option, style: style::Style, @@ -433,7 +433,7 @@ impl ColoredString { fn escape_inner_reset_sequences(&self) -> Cow { if !self.has_colors() || self.is_plain() { - return self.input.as_str().into(); + return self.input.clone(); } // TODO: BoyScoutRule @@ -445,10 +445,11 @@ impl ColoredString { .map(|(idx, _)| idx) .collect(); if matches.is_empty() { - return self.input.as_str().into(); + return self.input.clone(); } - let mut input = self.input.clone(); + let mut input_cow = self.input.clone(); + let input = input_cow.to_mut(); input.reserve(matches.len() * style.len()); for (idx_in_matches, offset) in matches.into_iter().enumerate() { @@ -462,14 +463,14 @@ impl ColoredString { } } - input.into() + input_cow } } impl Default for ColoredString { fn default() -> Self { ColoredString { - input: String::default(), + input: "".into(), fgcolor: None, bgcolor: None, style: style::CLEAR, @@ -487,7 +488,8 @@ impl Deref for ColoredString { impl<'a> From<&'a str> for ColoredString { fn from(s: &'a str) -> Self { ColoredString { - input: String::from(s), + // XXX We'd like to avoid this. + input: String::from(s).into(), ..ColoredString::default() } } @@ -553,7 +555,8 @@ impl<'a> Colorize for &'a str { fn color>(self, color: S) -> ColoredString { ColoredString { fgcolor: Some(color.into()), - input: String::from(self), + // input: self.into(), BOO XXX + input: String::from(self).into(), ..ColoredString::default() } } @@ -561,14 +564,16 @@ impl<'a> Colorize for &'a str { fn on_color>(self, color: S) -> ColoredString { ColoredString { bgcolor: Some(color.into()), - input: String::from(self), + // input: self.into(), + // XXX: Boo + input: String::from(self).into(), ..ColoredString::default() } } fn clear(self) -> ColoredString { ColoredString { - input: String::from(self), + input: String::from(self).into(), style: style::CLEAR, ..ColoredString::default() } @@ -608,7 +613,7 @@ impl<'a> Colorize for &'a str { impl fmt::Display for ColoredString { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if !self.has_colors() || self.is_plain() { - return ::fmt(&self.input, f); + return ::fmt(&self.input, f); } // XXX: see tests. Useful when nesting colored strings From 2f2ddbcd3a9d4521332e09cdc56ec1bfadd943f0 Mon Sep 17 00:00:00 2001 From: Shane Celis Date: Thu, 8 Jun 2023 07:16:46 -0400 Subject: [PATCH 04/11] feature: Cows. Cows everywhere! --- src/lib.rs | 191 ++++++++++++++++++++++++++--------------------------- 1 file changed, 94 insertions(+), 97 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 27bb561..c3c4212 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,8 +52,8 @@ pub use style::{Style, Styles}; /// A string that may have color and/or style applied to it. #[derive(Clone, Debug, PartialEq, Eq)] -pub struct ColoredString { - input: Cow<'static, str>, +pub struct ColoredString<'a> { + input: Cow<'a, str>, fgcolor: Option, bgcolor: Option, style: style::Style, @@ -64,123 +64,123 @@ pub struct ColoredString { /// You can use `colored` effectively simply by importing this trait /// and then using its methods on `String` and `&str`. #[allow(missing_docs)] -pub trait Colorize { +pub trait Colorize<'a> { // Font Colors - fn black(self) -> ColoredString + fn black(self) -> ColoredString<'a> where Self: Sized, { self.color(Color::Black) } - fn red(self) -> ColoredString + fn red(self) -> ColoredString<'a> where Self: Sized, { self.color(Color::Red) } - fn green(self) -> ColoredString + fn green(self) -> ColoredString<'a> where Self: Sized, { self.color(Color::Green) } - fn yellow(self) -> ColoredString + fn yellow(self) -> ColoredString<'a> where Self: Sized, { self.color(Color::Yellow) } - fn blue(self) -> ColoredString + fn blue(self) -> ColoredString<'a> where Self: Sized, { self.color(Color::Blue) } - fn magenta(self) -> ColoredString + fn magenta(self) -> ColoredString<'a> where Self: Sized, { self.color(Color::Magenta) } - fn purple(self) -> ColoredString + fn purple(self) -> ColoredString<'a> where Self: Sized, { self.color(Color::Magenta) } - fn cyan(self) -> ColoredString + fn cyan(self) -> ColoredString<'a> where Self: Sized, { self.color(Color::Cyan) } - fn white(self) -> ColoredString + fn white(self) -> ColoredString<'a> where Self: Sized, { self.color(Color::White) } - fn bright_black(self) -> ColoredString + fn bright_black(self) -> ColoredString<'a> where Self: Sized, { self.color(Color::BrightBlack) } - fn bright_red(self) -> ColoredString + fn bright_red(self) -> ColoredString<'a> where Self: Sized, { self.color(Color::BrightRed) } - fn bright_green(self) -> ColoredString + fn bright_green(self) -> ColoredString<'a> where Self: Sized, { self.color(Color::BrightGreen) } - fn bright_yellow(self) -> ColoredString + fn bright_yellow(self) -> ColoredString<'a> where Self: Sized, { self.color(Color::BrightYellow) } - fn bright_blue(self) -> ColoredString + fn bright_blue(self) -> ColoredString<'a> where Self: Sized, { self.color(Color::BrightBlue) } - fn bright_magenta(self) -> ColoredString + fn bright_magenta(self) -> ColoredString<'a> where Self: Sized, { self.color(Color::BrightMagenta) } - fn bright_purple(self) -> ColoredString + fn bright_purple(self) -> ColoredString<'a> where Self: Sized, { self.color(Color::BrightMagenta) } - fn bright_cyan(self) -> ColoredString + fn bright_cyan(self) -> ColoredString<'a> where Self: Sized, { self.color(Color::BrightCyan) } - fn bright_white(self) -> ColoredString + fn bright_white(self) -> ColoredString<'a> where Self: Sized, { self.color(Color::BrightWhite) } - fn truecolor(self, r: u8, g: u8, b: u8) -> ColoredString + fn truecolor(self, r: u8, g: u8, b: u8) -> ColoredString<'a> where Self: Sized, { self.color(Color::TrueColor { r, g, b }) } - fn custom_color(self, color: CustomColor) -> ColoredString + fn custom_color(self, color: CustomColor) -> ColoredString<'a> where Self: Sized, { @@ -190,123 +190,123 @@ pub trait Colorize { b: color.b, }) } - fn color>(self, color: S) -> ColoredString; + fn color>(self, color: S) -> ColoredString<'a>; // Background Colors - fn on_black(self) -> ColoredString + fn on_black(self) -> ColoredString<'a> where Self: Sized, { self.on_color(Color::Black) } - fn on_red(self) -> ColoredString + fn on_red(self) -> ColoredString<'a> where Self: Sized, { self.on_color(Color::Red) } - fn on_green(self) -> ColoredString + fn on_green(self) -> ColoredString<'a> where Self: Sized, { self.on_color(Color::Green) } - fn on_yellow(self) -> ColoredString + fn on_yellow(self) -> ColoredString<'a> where Self: Sized, { self.on_color(Color::Yellow) } - fn on_blue(self) -> ColoredString + fn on_blue(self) -> ColoredString<'a> where Self: Sized, { self.on_color(Color::Blue) } - fn on_magenta(self) -> ColoredString + fn on_magenta(self) -> ColoredString<'a> where Self: Sized, { self.on_color(Color::Magenta) } - fn on_purple(self) -> ColoredString + fn on_purple(self) -> ColoredString<'a> where Self: Sized, { self.on_color(Color::Magenta) } - fn on_cyan(self) -> ColoredString + fn on_cyan(self) -> ColoredString<'a> where Self: Sized, { self.on_color(Color::Cyan) } - fn on_white(self) -> ColoredString + fn on_white(self) -> ColoredString<'a> where Self: Sized, { self.on_color(Color::White) } - fn on_bright_black(self) -> ColoredString + fn on_bright_black(self) -> ColoredString<'a> where Self: Sized, { self.on_color(Color::BrightBlack) } - fn on_bright_red(self) -> ColoredString + fn on_bright_red(self) -> ColoredString<'a> where Self: Sized, { self.on_color(Color::BrightRed) } - fn on_bright_green(self) -> ColoredString + fn on_bright_green(self) -> ColoredString<'a> where Self: Sized, { self.on_color(Color::BrightGreen) } - fn on_bright_yellow(self) -> ColoredString + fn on_bright_yellow(self) -> ColoredString<'a> where Self: Sized, { self.on_color(Color::BrightYellow) } - fn on_bright_blue(self) -> ColoredString + fn on_bright_blue(self) -> ColoredString<'a> where Self: Sized, { self.on_color(Color::BrightBlue) } - fn on_bright_magenta(self) -> ColoredString + fn on_bright_magenta(self) -> ColoredString<'a> where Self: Sized, { self.on_color(Color::BrightMagenta) } - fn on_bright_purple(self) -> ColoredString + fn on_bright_purple(self) -> ColoredString<'a> where Self: Sized, { self.on_color(Color::BrightMagenta) } - fn on_bright_cyan(self) -> ColoredString + fn on_bright_cyan(self) -> ColoredString<'a> where Self: Sized, { self.on_color(Color::BrightCyan) } - fn on_bright_white(self) -> ColoredString + fn on_bright_white(self) -> ColoredString<'a> where Self: Sized, { self.on_color(Color::BrightWhite) } - fn on_truecolor(self, r: u8, g: u8, b: u8) -> ColoredString + fn on_truecolor(self, r: u8, g: u8, b: u8) -> ColoredString<'a> where Self: Sized, { self.on_color(Color::TrueColor { r, g, b }) } - fn on_custom_color(self, color: CustomColor) -> ColoredString + fn on_custom_color(self, color: CustomColor) -> ColoredString<'a> where Self: Sized, { @@ -316,25 +316,25 @@ pub trait Colorize { b: color.b, }) } - fn on_color>(self, color: S) -> ColoredString; + fn on_color>(self, color: S) -> ColoredString<'a>; // Styles - fn clear(self) -> ColoredString; - fn normal(self) -> ColoredString; - fn bold(self) -> ColoredString; - fn dimmed(self) -> ColoredString; - fn italic(self) -> ColoredString; - fn underline(self) -> ColoredString; - fn blink(self) -> ColoredString; + fn clear(self) -> ColoredString<'a>; + fn normal(self) -> ColoredString<'a>; + fn bold(self) -> ColoredString<'a>; + fn dimmed(self) -> ColoredString<'a>; + fn italic(self) -> ColoredString<'a>; + fn underline(self) -> ColoredString<'a>; + fn blink(self) -> ColoredString<'a>; /// Historical name of `Colorize::reversed`. May be removed in a future version. Please use /// `Colorize::reversed` instead - fn reverse(self) -> ColoredString; + fn reverse(self) -> ColoredString<'a>; /// This should be preferred to `Colorize::reverse`. - fn reversed(self) -> ColoredString; - fn hidden(self) -> ColoredString; - fn strikethrough(self) -> ColoredString; + fn reversed(self) -> ColoredString<'a>; + fn hidden(self) -> ColoredString<'a>; + fn strikethrough(self) -> ColoredString<'a>; } -impl ColoredString { +impl<'a> ColoredString<'a> { /// Get the current background color applied. /// /// ```rust @@ -467,7 +467,7 @@ impl ColoredString { } } -impl Default for ColoredString { +impl Default for ColoredString<'static> { fn default() -> Self { ColoredString { input: "".into(), @@ -478,139 +478,136 @@ impl Default for ColoredString { } } -impl Deref for ColoredString { +impl<'a> Deref for ColoredString<'a> { type Target = str; fn deref(&self) -> &str { &self.input } } -impl<'a> From<&'a str> for ColoredString { - fn from(s: &'a str) -> Self { +impl<'a, T: Into>> From for ColoredString<'a> { + fn from(s: T) -> Self { ColoredString { // XXX We'd like to avoid this. - input: String::from(s).into(), + input: s.into(), ..ColoredString::default() } } } -impl Colorize for ColoredString { - fn color>(mut self, color: S) -> ColoredString { +impl<'a> Colorize<'a> for ColoredString<'a> { + fn color>(mut self, color: S) -> ColoredString<'a> { self.fgcolor = Some(color.into()); self } - fn on_color>(mut self, color: S) -> ColoredString { + fn on_color>(mut self, color: S) -> ColoredString<'a> { self.bgcolor = Some(color.into()); self } - fn clear(self) -> ColoredString { + fn clear(self) -> ColoredString<'a> { ColoredString { input: self.input, ..ColoredString::default() } } - fn normal(self) -> ColoredString { + fn normal(self) -> ColoredString<'a> { self.clear() } - fn bold(mut self) -> ColoredString { + fn bold(mut self) -> ColoredString<'a> { self.style.add(style::Styles::Bold); self } - fn dimmed(mut self) -> ColoredString { + fn dimmed(mut self) -> ColoredString<'a> { self.style.add(style::Styles::Dimmed); self } - fn italic(mut self) -> ColoredString { + fn italic(mut self) -> ColoredString<'a> { self.style.add(style::Styles::Italic); self } - fn underline(mut self) -> ColoredString { + fn underline(mut self) -> ColoredString<'a> { self.style.add(style::Styles::Underline); self } - fn blink(mut self) -> ColoredString { + fn blink(mut self) -> ColoredString<'a> { self.style.add(style::Styles::Blink); self } - fn reverse(self) -> ColoredString { + fn reverse(self) -> ColoredString<'a> { self.reversed() } - fn reversed(mut self) -> ColoredString { + fn reversed(mut self) -> ColoredString<'a> { self.style.add(style::Styles::Reversed); self } - fn hidden(mut self) -> ColoredString { + fn hidden(mut self) -> ColoredString<'a> { self.style.add(style::Styles::Hidden); self } - fn strikethrough(mut self) -> ColoredString { + fn strikethrough(mut self) -> ColoredString<'a> { self.style.add(style::Styles::Strikethrough); self } } -impl<'a> Colorize for &'a str { - fn color>(self, color: S) -> ColoredString { +impl<'a, T: Into>> Colorize<'a> for T { + fn color>(self, color: S) -> ColoredString<'a> { ColoredString { fgcolor: Some(color.into()), - // input: self.into(), BOO XXX - input: String::from(self).into(), + input: self.into(), ..ColoredString::default() } } - fn on_color>(self, color: S) -> ColoredString { + fn on_color>(self, color: S) -> ColoredString<'a> { ColoredString { bgcolor: Some(color.into()), - // input: self.into(), - // XXX: Boo - input: String::from(self).into(), + input: self.into(), ..ColoredString::default() } } - fn clear(self) -> ColoredString { + fn clear(self) -> ColoredString<'a> { ColoredString { - input: String::from(self).into(), + input: self.into(), style: style::CLEAR, ..ColoredString::default() } } - fn normal(self) -> ColoredString { + fn normal(self) -> ColoredString<'a> { self.clear() } - fn bold(self) -> ColoredString { + fn bold(self) -> ColoredString<'a> { ColoredString::from(self).bold() } - fn dimmed(self) -> ColoredString { + fn dimmed(self) -> ColoredString<'a> { ColoredString::from(self).dimmed() } - fn italic(self) -> ColoredString { + fn italic(self) -> ColoredString<'a> { ColoredString::from(self).italic() } - fn underline(self) -> ColoredString { + fn underline(self) -> ColoredString<'a> { ColoredString::from(self).underline() } - fn blink(self) -> ColoredString { + fn blink(self) -> ColoredString<'a> { ColoredString::from(self).blink() } - fn reverse(self) -> ColoredString { + fn reverse(self) -> ColoredString<'a> { self.reversed() } - fn reversed(self) -> ColoredString { + fn reversed(self) -> ColoredString<'a> { ColoredString::from(self).reversed() } - fn hidden(self) -> ColoredString { + fn hidden(self) -> ColoredString<'a> { ColoredString::from(self).hidden() } - fn strikethrough(self) -> ColoredString { + fn strikethrough(self) -> ColoredString<'a> { ColoredString::from(self).strikethrough() } } -impl fmt::Display for ColoredString { +impl<'a> fmt::Display for ColoredString<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if !self.has_colors() || self.is_plain() { return ::fmt(&self.input, f); From 5efbafbea310b4b5e0ec244a7008381808fc1971 Mon Sep 17 00:00:00 2001 From: Shane Celis Date: Thu, 8 Jun 2023 07:17:09 -0400 Subject: [PATCH 05/11] feature: Print the dynamic_colors.rs. --- examples/dynamic_colors.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/dynamic_colors.rs b/examples/dynamic_colors.rs index d9861f4..17216af 100644 --- a/examples/dynamic_colors.rs +++ b/examples/dynamic_colors.rs @@ -3,12 +3,12 @@ use colored::*; fn main() { // the easy way - "blue string yo".color("blue"); + println!("{}", "blue string yo".color("blue")); // this will default to white - "white string".color("zorglub"); + println!("{}", "white string".color("zorglub")); // the safer way via a Result let color_res = "zorglub".parse(); // <- this returns a Result - "red string".color(color_res.unwrap_or(Color::Red)); + println!("{}", "red string".color(color_res.unwrap_or(Color::Red))); } From 60dfa951c98c70cffa5b094526fe2a770b245f32 Mon Sep 17 00:00:00 2001 From: Shane Celis Date: Fri, 9 Jun 2023 07:24:18 -0400 Subject: [PATCH 06/11] bug: Handle conditional compile correctly. --- src/control.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/control.rs b/src/control.rs index 5177a56..7391e32 100644 --- a/src/control.rs +++ b/src/control.rs @@ -103,12 +103,13 @@ impl ShouldColorize { /// `CLICOLOR_FORCE` takes highest priority, followed by `NO_COLOR`, /// followed by `CLICOLOR` combined with tty check. pub fn from_env() -> Self { - let tty: bool = if cfg!(feature = "tty") { - atty::is(atty::Stream::Stdout) - } else { - false - }; - + #[allow(unused_mut)] + #[allow(unused_assignments)] + let mut tty = false; + #[cfg(feature = "tty")] + { + tty = atty::is(atty::Stream::Stdout); + } ShouldColorize { clicolor: ShouldColorize::normalize_env(env::var("CLICOLOR")).unwrap_or(true) && tty, From 1ab549c3a03755545223050fc632461d330f35c8 Mon Sep 17 00:00:00 2001 From: Shane Celis Date: Sat, 24 Jun 2023 15:21:25 -0400 Subject: [PATCH 07/11] feature: Add Deref impl for ColoredStrings. --- src/lib.rs | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c3c4212..00d9a17 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,6 +39,7 @@ mod color; pub mod control; mod style; +// use std::str::pattern::Pattern; pub use self::customcolors::CustomColor; /// Custom colors support. @@ -46,19 +47,63 @@ pub mod customcolors; pub use color::*; -use std::{borrow::Cow, fmt, ops::Deref}; +use std::{borrow::Cow, fmt, ops::Deref, ops::DerefMut}; pub use style::{Style, Styles}; /// A string that may have color and/or style applied to it. #[derive(Clone, Debug, PartialEq, Eq)] pub struct ColoredString<'a> { - input: Cow<'a, str>, + pub input: Cow<'a, str>, fgcolor: Option, bgcolor: Option, style: style::Style, } +/// A collection of colored strings. +#[derive(Clone, Debug, PartialEq, Eq, Default)] +pub struct ColoredStrings<'a>(pub Vec>); + +impl<'a> Deref for ColoredStrings<'a> { + type Target = Vec>; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'a> DerefMut for ColoredStrings<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl<'a> ColoredStrings<'a> { + fn new() -> Self { + ColoredStrings::default() + } + + pub fn split(self, pat: char) -> Vec> { + let mut accum: Vec = Vec::new(); + accum.push(ColoredStrings::default()); + for colored_string in self.0 { + let mut i = colored_string.split(pat); + if let Some(mut first) = i.next() { + accum.last_mut().unwrap().push(first); + } + accum.extend(i.map(|s| { + ColoredStrings(vec![s]) + })); + } + accum + } +} + +impl<'a, T> From for ColoredStrings<'a> where T: Into> { + fn from(item: T) -> Self { + Self(vec![item.into().into()]) + } +} + /// The trait that enables something to be given color. /// /// You can use `colored` effectively simply by importing this trait @@ -465,6 +510,25 @@ impl<'a> ColoredString<'a> { input_cow } + + // We can use Pattern once it becomes stable. + // fn split>(&'a self, pat: P) -> impl Iterator>{ + fn split(self, pat: char) -> impl Iterator> { + let mut a = None; + let mut b = None; + if self.input.find(pat).is_none() { + a = Some(self); + } else { + b = Some(self.input.split(pat).map(move |s| ColoredString { + input: s.to_owned().into(), + fgcolor: self.fgcolor.clone(), + bgcolor: self.bgcolor.clone(), + style: self.style.clone() + }).collect::>()); + } + // Here's how you coalesce two iterators, only one of which has content. + a.into_iter().chain(b.into_iter().flatten()) + } } impl Default for ColoredString<'static> { @@ -623,6 +687,15 @@ impl<'a> fmt::Display for ColoredString<'a> { } } +impl<'a> fmt::Display for ColoredStrings<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for s in &self.0 { + s.fmt(f)? + } + Ok(()) + } +} + #[cfg(test)] mod tests { use super::*; From 44d12f8e55e51caccf73f41d226b0addb29efb95 Mon Sep 17 00:00:00 2001 From: Shane Celis Date: Sun, 25 Jun 2023 15:41:15 -0400 Subject: [PATCH 08/11] doc: Add doc strings. --- src/lib.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 00d9a17..d6e11ef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,13 +54,14 @@ pub use style::{Style, Styles}; /// A string that may have color and/or style applied to it. #[derive(Clone, Debug, PartialEq, Eq)] pub struct ColoredString<'a> { + /// The string to be styled. pub input: Cow<'a, str>, fgcolor: Option, bgcolor: Option, style: style::Style, } -/// A collection of colored strings. +/// A collection of colored strings. It can be used like a `Vec`. #[derive(Clone, Debug, PartialEq, Eq, Default)] pub struct ColoredStrings<'a>(pub Vec>); @@ -78,16 +79,18 @@ impl<'a> DerefMut for ColoredStrings<'a> { } impl<'a> ColoredStrings<'a> { - fn new() -> Self { + /// Return a new empty ColoredStrings container. + pub fn new() -> Self { ColoredStrings::default() } + /// Split a ColoredStrings by a character. pub fn split(self, pat: char) -> Vec> { let mut accum: Vec = Vec::new(); accum.push(ColoredStrings::default()); for colored_string in self.0 { let mut i = colored_string.split(pat); - if let Some(mut first) = i.next() { + if let Some(first) = i.next() { accum.last_mut().unwrap().push(first); } accum.extend(i.map(|s| { From 004268301b93e6ff4c67a193ed84b0fc0635ecf9 Mon Sep 17 00:00:00 2001 From: Shane Celis Date: Mon, 26 Jun 2023 17:45:37 -0400 Subject: [PATCH 09/11] refactor: Take advice from PR feedback. Thanks, @kpcyrd! --- src/control.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/control.rs b/src/control.rs index 7391e32..c708875 100644 --- a/src/control.rs +++ b/src/control.rs @@ -103,13 +103,14 @@ impl ShouldColorize { /// `CLICOLOR_FORCE` takes highest priority, followed by `NO_COLOR`, /// followed by `CLICOLOR` combined with tty check. pub fn from_env() -> Self { - #[allow(unused_mut)] - #[allow(unused_assignments)] - let mut tty = false; - #[cfg(feature = "tty")] - { - tty = atty::is(atty::Stream::Stdout); - } + let tty = { + #[cfg(feature = "tty")] + { + atty::is(atty::Stream::Stdout) + } + #[cfg(not(feature = "tty"))] + false + }; ShouldColorize { clicolor: ShouldColorize::normalize_env(env::var("CLICOLOR")).unwrap_or(true) && tty, From b368b88160a29a436943153c1b5ef82c2edb100a Mon Sep 17 00:00:00 2001 From: Shane Celis Date: Mon, 26 Jun 2023 17:57:15 -0400 Subject: [PATCH 10/11] revert: feature: Add Deref impl for ColoredStrings This reverts commit 1ab549c3a03755545223050fc632461d330f35c8. --- src/lib.rs | 82 ++---------------------------------------------------- 1 file changed, 3 insertions(+), 79 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d6e11ef..3862bb1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,7 +39,6 @@ mod color; pub mod control; mod style; -// use std::str::pattern::Pattern; pub use self::customcolors::CustomColor; /// Custom colors support. @@ -47,66 +46,19 @@ pub mod customcolors; pub use color::*; -use std::{borrow::Cow, fmt, ops::Deref, ops::DerefMut}; +use std::{borrow::Cow, fmt, ops::Deref}; pub use style::{Style, Styles}; /// A string that may have color and/or style applied to it. #[derive(Clone, Debug, PartialEq, Eq)] pub struct ColoredString<'a> { - /// The string to be styled. - pub input: Cow<'a, str>, + input: Cow<'a, str>, fgcolor: Option, bgcolor: Option, style: style::Style, } -/// A collection of colored strings. It can be used like a `Vec`. -#[derive(Clone, Debug, PartialEq, Eq, Default)] -pub struct ColoredStrings<'a>(pub Vec>); - -impl<'a> Deref for ColoredStrings<'a> { - type Target = Vec>; - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl<'a> DerefMut for ColoredStrings<'a> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl<'a> ColoredStrings<'a> { - /// Return a new empty ColoredStrings container. - pub fn new() -> Self { - ColoredStrings::default() - } - - /// Split a ColoredStrings by a character. - pub fn split(self, pat: char) -> Vec> { - let mut accum: Vec = Vec::new(); - accum.push(ColoredStrings::default()); - for colored_string in self.0 { - let mut i = colored_string.split(pat); - if let Some(first) = i.next() { - accum.last_mut().unwrap().push(first); - } - accum.extend(i.map(|s| { - ColoredStrings(vec![s]) - })); - } - accum - } -} - -impl<'a, T> From for ColoredStrings<'a> where T: Into> { - fn from(item: T) -> Self { - Self(vec![item.into().into()]) - } -} - /// The trait that enables something to be given color. /// /// You can use `colored` effectively simply by importing this trait @@ -513,25 +465,6 @@ impl<'a> ColoredString<'a> { input_cow } - - // We can use Pattern once it becomes stable. - // fn split>(&'a self, pat: P) -> impl Iterator>{ - fn split(self, pat: char) -> impl Iterator> { - let mut a = None; - let mut b = None; - if self.input.find(pat).is_none() { - a = Some(self); - } else { - b = Some(self.input.split(pat).map(move |s| ColoredString { - input: s.to_owned().into(), - fgcolor: self.fgcolor.clone(), - bgcolor: self.bgcolor.clone(), - style: self.style.clone() - }).collect::>()); - } - // Here's how you coalesce two iterators, only one of which has content. - a.into_iter().chain(b.into_iter().flatten()) - } } impl Default for ColoredString<'static> { @@ -690,15 +623,6 @@ impl<'a> fmt::Display for ColoredString<'a> { } } -impl<'a> fmt::Display for ColoredStrings<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - for s in &self.0 { - s.fmt(f)? - } - Ok(()) - } -} - #[cfg(test)] mod tests { use super::*; @@ -852,7 +776,7 @@ mod tests { #[test] fn escape_reset_sequence_spec_should_do_nothing_on_string_with_no_reset() { let style = ColoredString { - input: String::from("hello world !"), + input: ("hello world !").into(), ..ColoredString::default() }; From 3fd220b4b4e7df610cb342d0ab3b33d0b65b573a Mon Sep 17 00:00:00 2001 From: Shane Celis Date: Mon, 26 Jun 2023 18:16:12 -0400 Subject: [PATCH 11/11] refactor: Changed "no-color" feature to "color"... Cargo recommends features be additive. And when I tried testing without this commit with the following commands: ```sh cargo test --all-features cargo test --no-default-features cargo test ``` Some were breaking on account of "no_color". Updated the README. --- Cargo.toml | 4 ++-- README.md | 8 ++++++-- src/lib.rs | 26 +++++++++++++------------- tests/ansi_term_compat.rs | 9 +++++---- 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cdd3062..9362c0d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,10 +11,10 @@ readme = "README.md" keywords = ["color", "string", "term", "ansi_term", "term-painter"] [features] -default = ["tty"] +default = ["tty", "color"] tty = ["dep:atty"] # with this feature, no color will ever be written -no-color = [] +color = [] [dependencies] atty = { version = "0.2", optional = true } diff --git a/README.md b/README.md index 8c4a71c..65edbc2 100644 --- a/README.md +++ b/README.md @@ -118,10 +118,14 @@ using the `no-color` feature. For example, you can do this in your `Cargo.toml` to disable color in tests: ```toml +[dependencies] +colored = { version = "2", default-features = false, features = [ "tty" ] } + [features] -# this effectively enable the feature `no-color` of colored when testing with +# this effectively disables the feature `color` of colored when testing with # `cargo test --feature dumb_terminal` -dumb_terminal = ["colored/no-color"] +dumb_terminal = ["colored"] +color_terminal = ["colored/color"] ``` You can use have even finer control by using the diff --git a/src/lib.rs b/src/lib.rs index 3862bb1..62d0bf3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -387,12 +387,12 @@ impl<'a> ColoredString<'a> { self.bgcolor.is_none() && self.fgcolor.is_none() && self.style == style::CLEAR } - #[cfg(not(feature = "no-color"))] + #[cfg(feature = "color")] fn has_colors(&self) -> bool { control::SHOULD_COLORIZE.should_colorize() } - #[cfg(feature = "no-color")] + #[cfg(not(feature = "color"))] fn has_colors(&self) -> bool { false } @@ -685,7 +685,7 @@ mod tests { assert_eq!("", "".clear().compute_style()); } - #[cfg_attr(feature = "no-color", ignore)] + #[cfg(feature = "color")] #[test] fn compute_style_simple_fg_blue() { let blue = "\x1B[34m"; @@ -693,7 +693,7 @@ mod tests { assert_eq!(blue, "".blue().compute_style()); } - #[cfg_attr(feature = "no-color", ignore)] + #[cfg(feature = "color")] #[test] fn compute_style_simple_bg_blue() { let on_blue = "\x1B[44m"; @@ -701,7 +701,7 @@ mod tests { assert_eq!(on_blue, "".on_blue().compute_style()); } - #[cfg_attr(feature = "no-color", ignore)] + #[cfg(feature = "color")] #[test] fn compute_style_blue_on_blue() { let blue_on_blue = "\x1B[44;34m"; @@ -709,7 +709,7 @@ mod tests { assert_eq!(blue_on_blue, "".blue().on_blue().compute_style()); } - #[cfg_attr(feature = "no-color", ignore)] + #[cfg(feature = "color")] #[test] fn compute_style_simple_fg_bright_blue() { let blue = "\x1B[94m"; @@ -717,7 +717,7 @@ mod tests { assert_eq!(blue, "".bright_blue().compute_style()); } - #[cfg_attr(feature = "no-color", ignore)] + #[cfg(feature = "color")] #[test] fn compute_style_simple_bg_bright_blue() { let on_blue = "\x1B[104m"; @@ -725,7 +725,7 @@ mod tests { assert_eq!(on_blue, "".on_bright_blue().compute_style()); } - #[cfg_attr(feature = "no-color", ignore)] + #[cfg(feature = "color")] #[test] fn compute_style_bright_blue_on_bright_blue() { let blue_on_blue = "\x1B[104;94m"; @@ -736,7 +736,7 @@ mod tests { ); } - #[cfg_attr(feature = "no-color", ignore)] + #[cfg(feature = "color")] #[test] fn compute_style_simple_bold() { let bold = "\x1B[1m"; @@ -744,7 +744,7 @@ mod tests { assert_eq!(bold, "".bold().compute_style()); } - #[cfg_attr(feature = "no-color", ignore)] + #[cfg(feature = "color")] #[test] fn compute_style_blue_bold() { let blue_bold = "\x1B[1;34m"; @@ -752,7 +752,7 @@ mod tests { assert_eq!(blue_bold, "".blue().bold().compute_style()); } - #[cfg_attr(feature = "no-color", ignore)] + #[cfg(feature = "color")] #[test] fn compute_style_blue_bold_on_blue() { let blue_bold_on_blue = "\x1B[1;44;34m"; @@ -786,7 +786,7 @@ mod tests { assert_eq!(expected, output); } - #[cfg_attr(feature = "no-color", ignore)] + #[cfg(feature = "color")] #[test] fn escape_reset_sequence_spec_should_replace_inner_reset_sequence_with_current_style() { let input = format!("start {} end", String::from("hello world !").red()); @@ -800,7 +800,7 @@ mod tests { assert_eq!(expected, output); } - #[cfg_attr(feature = "no-color", ignore)] + #[cfg(feature = "color")] #[test] fn escape_reset_sequence_spec_should_replace_multiple_inner_reset_sequences_with_current_style() { diff --git a/tests/ansi_term_compat.rs b/tests/ansi_term_compat.rs index 6683ee0..4795053 100644 --- a/tests/ansi_term_compat.rs +++ b/tests/ansi_term_compat.rs @@ -1,4 +1,4 @@ -#![cfg(not(feature = "no-color"))] +#![cfg(feature = "color")] #![allow(unused_imports)] extern crate ansi_term; @@ -12,8 +12,9 @@ macro_rules! test_simple_color { #[test] fn $colored_name() { let s = format!("{} {}", $string, stringify!($colored_name)); + assert_eq!( - s.$colored_name().to_string(), + s.clone().$colored_name().to_string(), Colour::$ansi_term_name.paint(s).to_string() ) } @@ -40,7 +41,7 @@ macro_rules! test_simple_style { fn $style() { let s = format!("{} {}", $string, stringify!($style)); assert_eq!( - s.$style().to_string(), + s.clone().$style().to_string(), ansi_term::Style::new().$style().paint(s).to_string() ) } @@ -68,7 +69,7 @@ macro_rules! test_simple_bgcolor { fn $colored_name() { let s = format!("{} {}", $string, stringify!($colored_name)); assert_eq!( - s.$colored_name().to_string(), + s.clone().$colored_name().to_string(), ansi_term::Style::default() .on(ansi_term::Colour::$ansi_term_name) .paint(s)