From aa6d070b52f87505ed21f42ad87b52a0a6d950e2 Mon Sep 17 00:00:00 2001 From: Sophia Waggoner Date: Sat, 2 Dec 2023 22:46:15 -0800 Subject: [PATCH 01/33] Initial change (Deref + DerefMut for ColoredString). --- src/lib.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 71e042b..31b40c9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,7 +43,7 @@ pub mod customcolors; pub use color::*; -use std::{borrow::Cow, fmt, ops::Deref}; +use std::{borrow::Cow, fmt, ops::{Deref, DerefMut}}; pub use style::{Style, Styles}; @@ -473,12 +473,18 @@ impl Default for ColoredString { } impl Deref for ColoredString { - type Target = str; - fn deref(&self) -> &str { + type Target = String; + fn deref(&self) -> &String { &self.input } } +impl DerefMut for ColoredString { + fn deref_mut(&mut self) -> &mut String { + &mut self.input + } +} + impl<'a> From<&'a str> for ColoredString { fn from(s: &'a str) -> Self { ColoredString { From eeeb841efdc75b2bfe6a1b1331e3c85661bbb269 Mon Sep 17 00:00:00 2001 From: Sophia Waggoner Date: Sun, 3 Dec 2023 00:16:01 -0800 Subject: [PATCH 02/33] Add an example of modifying a ColoredString's text content with DerefMut. --- examples/modifying_colored_strings.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 examples/modifying_colored_strings.rs diff --git a/examples/modifying_colored_strings.rs b/examples/modifying_colored_strings.rs new file mode 100644 index 0000000..6be5a1f --- /dev/null +++ b/examples/modifying_colored_strings.rs @@ -0,0 +1,19 @@ +extern crate colored; +use colored::Colorize; + +fn main() { + let red_text = "This is Red!".color("red").italic().underline(); + println!("{}", red_text); + let blue_text = "This is Blue!".color("blue").italic().underline(); + println!("{}", blue_text); + // This seems inefficient. If only there were a way to change the contents + // of a ColoredString without having to make another ColoredString, + // complete with heap allocation and having to re-construct the style? + + let mut text = "This is Red!".color("red").italic().underline(); + println!("{}", text); + text.push_str(" ...but now it's blue!"); + text = text.blue(); + println!("{}", text); + // And look at that, we preserved the style AND the allocation! +} From 528ef772bdb215eedcfe99b3e80e01cbae7034d9 Mon Sep 17 00:00:00 2001 From: Sophia Waggoner Date: Sun, 3 Dec 2023 00:52:44 -0800 Subject: [PATCH 03/33] Add methods copy_fgcolor, copy_bgcolor, and copy_style to ColoredString. --- src/lib.rs | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 31b40c9..e554dea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -382,6 +382,53 @@ impl ColoredString { self.bgcolor.is_none() && self.fgcolor.is_none() && self.style == style::CLEAR } + /// Copies the foreground color of another ColoredString. + /// Default coloring counts as coloring. + /// + /// Copying from a `ColoredString` without foreground color + /// styling will erase its foreground color. + /// + /// ```rust + /// # use colored::*; + /// let cstr1 = "Red".color("red").on_color("white"); + /// let mut cstr2 = "Should be red.".color("blue").on_color("black"); + /// cstr2.copy_fgcolor(&cstr1); + /// assert_eq!(cstr1.fgcolor(), cstr2.fgcolor()); + /// assert_ne!(cstr1.bgcolor(), cstr2.bgcolor()); + /// ``` + pub fn copy_fgcolor(&mut self, other: &ColoredString) { + self.fgcolor = other.fgcolor.clone(); + } + + /// Copies the background color of another ColoredString. + /// Clear (no) background counts as a background. + /// + /// ```rust + /// # use colored::*; + /// let cstr1 = "Clear background (nice).".color("green"); + /// let mut cstr2 = "Santa Claus? Nope, not here.".color("red").on_color("green"); + /// cstr2.copy_bgcolor(&cstr1); + /// assert_eq!(cstr1.bgcolor(), cstr2.bgcolor()); + /// assert_ne!(cstr1.fgcolor(), cstr2.fgcolor()); + /// ``` + pub fn copy_bgcolor(&mut self, other: &ColoredString) { + self.bgcolor = other.bgcolor.clone(); + } + + /// Copies the style (not including colors) of another ColoredString. + /// + /// ```rust + /// # use colored::*; + /// let cstr1 = "Red".red().bold().italic().underline(); + /// let mut cstr2 = "blue".blue(); + /// cstr2.copy_style(&cstr1); + /// assert_eq!(cstr1.style(), cstr2.style()); + /// assert_ne!(cstr1.fgcolor(), cstr2.fgcolor()); + /// ``` + pub fn copy_style(&mut self, other: &ColoredString) { + self.style = other.style.clone(); + } + #[cfg(not(feature = "no-color"))] fn has_colors(&self) -> bool { control::SHOULD_COLORIZE.should_colorize() From b02b8643d30616d096f0deb4c35ab911d4a9c17c Mon Sep 17 00:00:00 2001 From: Sophia Waggoner Date: Sun, 3 Dec 2023 07:37:35 -0800 Subject: [PATCH 04/33] Create traits Colorized and CopyColorize, moved style copying functions from ColoredString into CopyColorize, and add StyleTemplate struct to assist with CopyColorize. --- src/lib.rs | 228 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 180 insertions(+), 48 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e554dea..271f113 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,7 +43,11 @@ pub mod customcolors; pub use color::*; -use std::{borrow::Cow, fmt, ops::{Deref, DerefMut}}; +use std::{ + borrow::Cow, + fmt, + ops::{Deref, DerefMut}, +}; pub use style::{Style, Styles}; @@ -56,6 +60,20 @@ pub struct ColoredString { style: style::Style, } +/// Simply possesses color and style and nothing more. +/// +/// Useful for acting as style templates or stamps for those +/// that implement [`CopyColorize`]. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct StyleTemplate { + /// Foreground color + pub fgcolor: Option, + /// Background color + pub bgcolor: Option, + /// Style + pub style: Style, +} + /// The trait that enables something to be given color. /// /// You can use `colored` effectively simply by importing this trait @@ -329,6 +347,69 @@ pub trait Colorize { fn strikethrough(self) -> ColoredString; } +/// Represents something that has colorization applied to it. +/// +/// Examples mainly include [`ColoredString`] and [`StyleTemplate`]. +#[allow(missing_docs)] +pub trait Colorized { + fn foreground_color(&self) -> Option; + + fn background_color(&self) -> Option; + + fn styling(&self) -> Style; +} + +/// Implementors can copy foreground color, background color, and +/// or style from types that implement [`Colorized`]. +pub trait CopyColorize { + /// Copies the foreground color of any value that implements + /// [`Colorized`]. + /// + /// Default coloring counts as coloring; copying from a + /// [`Colorized`] without foreground color + /// styling will erase its foreground color. + /// + /// ```rust + /// # use colored::*; + /// let cstr1 = "Red".color("red").on_color("white"); + /// let mut cstr2 = "Should be red.".color("blue").on_color("black"); + /// cstr2.copy_foreground_color(&cstr1); + /// assert_eq!(cstr1.foreground_color(), cstr2.foreground_color()); + /// assert_ne!(cstr1.background_color(), cstr2.background_color()); + /// ``` + fn copy_foreground_color(&mut self, other: &T); + + /// Copies the background color of another value that implements + /// [`Colorized`]. + /// + /// Clear (no) background counts as a background. + /// + /// ```rust + /// # use colored::*; + /// let cstr1 = "Clear background (nice).".color("green"); + /// let mut cstr2 = "Santa Claus? Nope, not here." + /// .color("red") + /// .on_color("green"); + /// cstr2.copy_background_color(&cstr1); + /// assert_eq!(cstr1.background_color(), cstr2.background_color()); + /// assert_ne!(cstr1.foreground_color(), cstr2.foreground_color()); + /// ``` + fn copy_background_color(&mut self, other: &T); + + /// Copies the style (not including colors) of another value + /// that implements [`Colorized`]. + /// + /// ```rust + /// # use colored::*; + /// let cstr1 = "Red".red().bold().italic().underline(); + /// let mut cstr2 = "blue".blue(); + /// cstr2.copy_styling(&cstr1); + /// assert_eq!(cstr1.styling(), cstr2.styling()); + /// assert_ne!(cstr1.foreground_color(), cstr2.foreground_color()); + /// ``` + fn copy_styling(&mut self, other: &T); +} + impl ColoredString { /// Get the current background color applied. /// @@ -339,6 +420,9 @@ impl ColoredString { /// let cstr = cstr.clear(); /// assert_eq!(cstr.fgcolor(), None); /// ``` + #[deprecated( + note = "Deprecated in favor of the `foreground_color` method of the `Colorized` trait." + )] pub fn fgcolor(&self) -> Option { self.fgcolor.as_ref().copied() } @@ -352,6 +436,9 @@ impl ColoredString { /// let cstr = cstr.clear(); /// assert_eq!(cstr.bgcolor(), None); /// ``` + #[deprecated( + note = "Deprecated in favor of the `background_color` method of the `Colorized` trait." + )] pub fn bgcolor(&self) -> Option { self.bgcolor.as_ref().copied() } @@ -365,6 +452,7 @@ impl ColoredString { /// assert_eq!(colored.style().contains(Styles::Italic), true); /// assert_eq!(colored.style().contains(Styles::Dimmed), false); /// ``` + #[deprecated(note = "Deprecated in favor of the `styling` method of the `Colorized` trait.")] pub fn style(&self) -> style::Style { self.style } @@ -382,53 +470,6 @@ impl ColoredString { self.bgcolor.is_none() && self.fgcolor.is_none() && self.style == style::CLEAR } - /// Copies the foreground color of another ColoredString. - /// Default coloring counts as coloring. - /// - /// Copying from a `ColoredString` without foreground color - /// styling will erase its foreground color. - /// - /// ```rust - /// # use colored::*; - /// let cstr1 = "Red".color("red").on_color("white"); - /// let mut cstr2 = "Should be red.".color("blue").on_color("black"); - /// cstr2.copy_fgcolor(&cstr1); - /// assert_eq!(cstr1.fgcolor(), cstr2.fgcolor()); - /// assert_ne!(cstr1.bgcolor(), cstr2.bgcolor()); - /// ``` - pub fn copy_fgcolor(&mut self, other: &ColoredString) { - self.fgcolor = other.fgcolor.clone(); - } - - /// Copies the background color of another ColoredString. - /// Clear (no) background counts as a background. - /// - /// ```rust - /// # use colored::*; - /// let cstr1 = "Clear background (nice).".color("green"); - /// let mut cstr2 = "Santa Claus? Nope, not here.".color("red").on_color("green"); - /// cstr2.copy_bgcolor(&cstr1); - /// assert_eq!(cstr1.bgcolor(), cstr2.bgcolor()); - /// assert_ne!(cstr1.fgcolor(), cstr2.fgcolor()); - /// ``` - pub fn copy_bgcolor(&mut self, other: &ColoredString) { - self.bgcolor = other.bgcolor.clone(); - } - - /// Copies the style (not including colors) of another ColoredString. - /// - /// ```rust - /// # use colored::*; - /// let cstr1 = "Red".red().bold().italic().underline(); - /// let mut cstr2 = "blue".blue(); - /// cstr2.copy_style(&cstr1); - /// assert_eq!(cstr1.style(), cstr2.style()); - /// assert_ne!(cstr1.fgcolor(), cstr2.fgcolor()); - /// ``` - pub fn copy_style(&mut self, other: &ColoredString) { - self.style = other.style.clone(); - } - #[cfg(not(feature = "no-color"))] fn has_colors(&self) -> bool { control::SHOULD_COLORIZE.should_colorize() @@ -597,6 +638,34 @@ impl Colorize for ColoredString { } } +impl Colorized for ColoredString { + fn foreground_color(&self) -> Option { + self.fgcolor + } + + fn background_color(&self) -> Option { + self.bgcolor + } + + fn styling(&self) -> Style { + self.style + } +} + +impl CopyColorize for ColoredString { + fn copy_foreground_color(&mut self, other: &T) { + self.fgcolor = other.foreground_color(); + } + + fn copy_background_color(&mut self, other: &T) { + self.bgcolor = other.background_color(); + } + + fn copy_styling(&mut self, other: &T) { + self.style = other.styling(); + } +} + impl<'a> Colorize for &'a str { fn color>(self, color: S) -> ColoredString { ColoredString { @@ -669,6 +738,34 @@ impl fmt::Display for ColoredString { } } +impl Colorized for StyleTemplate { + fn foreground_color(&self) -> Option { + self.fgcolor + } + + fn background_color(&self) -> Option { + self.bgcolor + } + + fn styling(&self) -> Style { + self.style + } +} + +impl CopyColorize for StyleTemplate { + fn copy_foreground_color(&mut self, other: &T) { + self.fgcolor = other.foreground_color(); + } + + fn copy_background_color(&mut self, other: &T) { + self.bgcolor = other.background_color(); + } + + fn copy_styling(&mut self, other: &T) { + self.style = other.styling(); + } +} + #[cfg(test)] mod tests { use super::*; @@ -893,6 +990,8 @@ mod tests { #[test] fn exposing_tests() { + #![allow(deprecated)] + let cstring = "".red(); assert_eq!(cstring.fgcolor(), Some(Color::Red)); assert_eq!(cstring.bgcolor(), None); @@ -912,4 +1011,37 @@ mod tests { assert_eq!(cstring.style().contains(Styles::Italic), true); assert_eq!(cstring.style().contains(Styles::Dimmed), false); } + + #[test] + fn colorized_trait() { + let mut cstring = "".red(); + assert_eq!(cstring.foreground_color(), Some(Color::Red)); + assert_eq!(cstring.background_color(), None); + + cstring = "Ugly text".blue().on_red(); + assert_eq!(cstring.foreground_color(), Some(Color::Blue)); + assert_eq!(cstring.background_color(), Some(Color::Red)); + + cstring = "Shiny!".bold().italic(); + assert!(cstring.styling().contains(Styles::Bold)); + assert!(cstring.styling().contains(Styles::Italic)); + } + + #[test] + fn copy_colorize() { + let mut cstring1 = "".red().bold(); + let mut cstring2 = "".blue().italic(); + cstring2.copy_foreground_color(&cstring1); + assert_eq!(cstring2.foreground_color(), Some(Color::Red)); + // ... but did not copy the style. + assert!(!cstring2.styling().contains(Styles::Bold)); + + cstring1 = "".red().on_magenta(); + cstring2 = "".blue(); + cstring2.copy_background_color(&cstring1); + assert_eq!(cstring1.foreground_color(), Some(Color::Red)); + assert_eq!(cstring2.foreground_color(), Some(Color::Blue)); + assert_eq!(cstring1.background_color(), Some(Color::Magenta)); + assert_eq!(cstring2.background_color(), Some(Color::Magenta)); + } } From 6f530a3645259256eb6038f2604067d4176e0e96 Mon Sep 17 00:00:00 2001 From: Sophia Waggoner Date: Sun, 3 Dec 2023 14:17:47 -0800 Subject: [PATCH 05/33] Create, implement, and expose methods of creating and modifying Style values for the user. --- src/lib.rs | 12 ++- src/style.rs | 212 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 222 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 271f113..198ff42 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -555,7 +555,7 @@ impl Default for ColoredString { input: String::default(), fgcolor: None, bgcolor: None, - style: style::CLEAR, + style: Style::default(), } } } @@ -766,6 +766,16 @@ impl CopyColorize for StyleTemplate { } } +impl Default for StyleTemplate { + fn default() -> Self { + StyleTemplate { + fgcolor: None, + bgcolor: None, + style: Style::default() + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/style.rs b/src/style.rs index 8588554..e9014cb 100644 --- a/src/style.rs +++ b/src/style.rs @@ -1,3 +1,5 @@ +use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not}; + const CLEARV: u8 = 0b0000_0000; const BOLD: u8 = 0b0000_0001; const UNDERLINE: u8 = 0b0000_0010; @@ -110,9 +112,160 @@ impl Style { .join(";") } - pub(crate) fn add(&mut self, two: Styles) { + /// Adds the `two` style switch to this Style. + /// + /// ```rust + /// # use colored::*; + /// let cstr = "".red().bold(); + /// let mut template = StyleTemplate::default(); + /// template.copy_styling(&cstr); + /// template.style.add(Styles::Italic); + /// let mut cstr2 = "".blue(); + /// cstr2.copy_styling(&template); + /// assert!(cstr2.styling().contains(Styles::Bold)); + /// assert!(cstr2.styling().contains(Styles::Italic)); + /// ``` + pub fn add(&mut self, two: Styles) { self.0 |= two.to_u8(); } + + /// Turns off a style switch. + /// + /// ```rust + /// use colored::*; + /// let cstr = "".red().bold().italic(); + /// let mut template = StyleTemplate::default(); + /// template.copy_styling(&cstr); + /// template.style.remove(Styles::Italic); + /// let mut cstr2 = "".blue(); + /// cstr2.copy_styling(&template); + /// assert!(cstr2.styling().contains(Styles::Bold)); + /// assert!(!cstr2.styling().contains(Styles::Italic)); + /// ``` + pub fn remove(&mut self, two: Styles) { + self.0 &= !two.to_u8(); + } +} + +macro_rules! auto_impl_ref_binop_trait { + (impl $trait_name:ident, $method:ident for $t:ty) => { + impl $trait_name<&$t> for $t { + type Output = <$t as $trait_name<$t>>::Output; + + #[inline] + fn $method(self, rhs: &$t) -> Self::Output { + $trait_name::$method(self, *rhs) + } + } + + impl $trait_name<$t> for &$t { + type Output = <$t as $trait_name<$t>>::Output; + + #[inline] + fn $method(self, rhs: $t) -> Self::Output { + $trait_name::$method(*self, rhs) + } + } + + impl $trait_name<&$t> for &$t { + type Output = <$t as $trait_name<$t>>::Output; + + #[inline] + fn $method(self, rhs: &$t) -> Self::Output { + $trait_name::$method(*self, *rhs) + } + } + }; +} + +impl BitAnd