| 1 | use crate::ansi::RESET; |
| 2 | use crate::difference::Difference; |
| 3 | use crate::style::{Color, Style}; |
| 4 | use crate::write::AnyWrite; |
| 5 | use std::borrow::Cow; |
| 6 | use std::fmt; |
| 7 | use std::io; |
| 8 | |
| 9 | /// An `AnsiGenericString` includes a generic string type and a `Style` to |
| 10 | /// display that string. `AnsiString` and `AnsiByteString` are aliases for |
| 11 | /// this type on `str` and `\[u8]`, respectively. |
| 12 | #[derive (PartialEq, Debug)] |
| 13 | pub struct AnsiGenericString<'a, S: 'a + ToOwned + ?Sized> |
| 14 | where |
| 15 | <S as ToOwned>::Owned: fmt::Debug, |
| 16 | { |
| 17 | pub(crate) style: Style, |
| 18 | pub(crate) string: Cow<'a, S>, |
| 19 | } |
| 20 | |
| 21 | /// Cloning an `AnsiGenericString` will clone its underlying string. |
| 22 | /// |
| 23 | /// # Examples |
| 24 | /// |
| 25 | /// ``` |
| 26 | /// use nu_ansi_term::AnsiString; |
| 27 | /// |
| 28 | /// let plain_string = AnsiString::from("a plain string" ); |
| 29 | /// let clone_string = plain_string.clone(); |
| 30 | /// assert_eq!(clone_string, plain_string); |
| 31 | /// ``` |
| 32 | impl<'a, S: 'a + ToOwned + ?Sized> Clone for AnsiGenericString<'a, S> |
| 33 | where |
| 34 | <S as ToOwned>::Owned: fmt::Debug, |
| 35 | { |
| 36 | fn clone(&self) -> AnsiGenericString<'a, S> { |
| 37 | AnsiGenericString { |
| 38 | style: self.style, |
| 39 | string: self.string.clone(), |
| 40 | } |
| 41 | } |
| 42 | } |
| 43 | |
| 44 | // You might think that the hand-written Clone impl above is the same as the |
| 45 | // one that gets generated with #[derive]. But it’s not *quite* the same! |
| 46 | // |
| 47 | // `str` is not Clone, and the derived Clone implementation puts a Clone |
| 48 | // constraint on the S type parameter (generated using --pretty=expanded): |
| 49 | // |
| 50 | // ↓_________________↓ |
| 51 | // impl <'a, S: ::std::clone::Clone + 'a + ToOwned + ?Sized> ::std::clone::Clone |
| 52 | // for ANSIGenericString<'a, S> where |
| 53 | // <S as ToOwned>::Owned: fmt::Debug { ... } |
| 54 | // |
| 55 | // This resulted in compile errors when you tried to derive Clone on a type |
| 56 | // that used it: |
| 57 | // |
| 58 | // #[derive(PartialEq, Debug, Clone, Default)] |
| 59 | // pub struct TextCellContents(Vec<AnsiString<'static>>); |
| 60 | // ^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 61 | // error[E0277]: the trait `std::clone::Clone` is not implemented for `str` |
| 62 | // |
| 63 | // The hand-written impl above can ignore that constraint and still compile. |
| 64 | |
| 65 | /// An ANSI String is a string coupled with the `Style` to display it |
| 66 | /// in a terminal. |
| 67 | /// |
| 68 | /// Although not technically a string itself, it can be turned into |
| 69 | /// one with the `to_string` method. |
| 70 | /// |
| 71 | /// # Examples |
| 72 | /// |
| 73 | /// ``` |
| 74 | /// use nu_ansi_term::AnsiString; |
| 75 | /// use nu_ansi_term::Color::Red; |
| 76 | /// |
| 77 | /// let red_string = Red.paint("a red string" ); |
| 78 | /// println!("{}" , red_string); |
| 79 | /// ``` |
| 80 | /// |
| 81 | /// ``` |
| 82 | /// use nu_ansi_term::AnsiString; |
| 83 | /// |
| 84 | /// let plain_string = AnsiString::from("a plain string" ); |
| 85 | /// ``` |
| 86 | pub type AnsiString<'a> = AnsiGenericString<'a, str>; |
| 87 | |
| 88 | /// An `AnsiByteString` represents a formatted series of bytes. Use |
| 89 | /// `AnsiByteString` when styling text with an unknown encoding. |
| 90 | pub type AnsiByteString<'a> = AnsiGenericString<'a, [u8]>; |
| 91 | |
| 92 | impl<'a, I, S: 'a + ToOwned + ?Sized> From<I> for AnsiGenericString<'a, S> |
| 93 | where |
| 94 | I: Into<Cow<'a, S>>, |
| 95 | <S as ToOwned>::Owned: fmt::Debug, |
| 96 | { |
| 97 | fn from(input: I) -> AnsiGenericString<'a, S> { |
| 98 | AnsiGenericString { |
| 99 | string: input.into(), |
| 100 | style: Style::default(), |
| 101 | } |
| 102 | } |
| 103 | } |
| 104 | |
| 105 | impl<'a, S: 'a + ToOwned + ?Sized> AnsiGenericString<'a, S> |
| 106 | where |
| 107 | <S as ToOwned>::Owned: fmt::Debug, |
| 108 | { |
| 109 | /// Directly access the style |
| 110 | pub fn style_ref(&self) -> &Style { |
| 111 | &self.style |
| 112 | } |
| 113 | |
| 114 | /// Directly access the style mutably |
| 115 | pub fn style_ref_mut(&mut self) -> &mut Style { |
| 116 | &mut self.style |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | /// A set of `AnsiGenericStrings`s collected together, in order to be |
| 121 | /// written with a minimum of control characters. |
| 122 | #[derive (Debug, PartialEq)] |
| 123 | pub struct AnsiGenericStrings<'a, S: 'a + ToOwned + ?Sized>(pub &'a [AnsiGenericString<'a, S>]) |
| 124 | where |
| 125 | <S as ToOwned>::Owned: fmt::Debug, |
| 126 | S: PartialEq; |
| 127 | |
| 128 | /// A set of `AnsiString`s collected together, in order to be written with a |
| 129 | /// minimum of control characters. |
| 130 | pub type AnsiStrings<'a> = AnsiGenericStrings<'a, str>; |
| 131 | |
| 132 | /// A function to construct an `AnsiStrings` instance. |
| 133 | #[allow (non_snake_case)] |
| 134 | pub fn AnsiStrings<'a>(arg: &'a [AnsiString<'a>]) -> AnsiStrings<'a> { |
| 135 | AnsiGenericStrings(arg) |
| 136 | } |
| 137 | |
| 138 | /// A set of `AnsiByteString`s collected together, in order to be |
| 139 | /// written with a minimum of control characters. |
| 140 | pub type AnsiByteStrings<'a> = AnsiGenericStrings<'a, [u8]>; |
| 141 | |
| 142 | /// A function to construct an `AnsiByteStrings` instance. |
| 143 | #[allow (non_snake_case)] |
| 144 | pub fn AnsiByteStrings<'a>(arg: &'a [AnsiByteString<'a>]) -> AnsiByteStrings<'a> { |
| 145 | AnsiGenericStrings(arg) |
| 146 | } |
| 147 | |
| 148 | // ---- paint functions ---- |
| 149 | |
| 150 | impl Style { |
| 151 | /// Paints the given text with this color, returning an ANSI string. |
| 152 | #[must_use ] |
| 153 | pub fn paint<'a, I, S: 'a + ToOwned + ?Sized>(self, input: I) -> AnsiGenericString<'a, S> |
| 154 | where |
| 155 | I: Into<Cow<'a, S>>, |
| 156 | <S as ToOwned>::Owned: fmt::Debug, |
| 157 | { |
| 158 | AnsiGenericString { |
| 159 | string: input.into(), |
| 160 | style: self, |
| 161 | } |
| 162 | } |
| 163 | } |
| 164 | |
| 165 | impl Color { |
| 166 | /// Paints the given text with this color, returning an ANSI string. |
| 167 | /// This is a short-cut so you don’t have to use `Blue.normal()` just |
| 168 | /// to get blue text. |
| 169 | /// |
| 170 | /// ``` |
| 171 | /// use nu_ansi_term::Color::Blue; |
| 172 | /// println!("{}" , Blue.paint("da ba dee" )); |
| 173 | /// ``` |
| 174 | #[must_use ] |
| 175 | pub fn paint<'a, I, S: 'a + ToOwned + ?Sized>(self, input: I) -> AnsiGenericString<'a, S> |
| 176 | where |
| 177 | I: Into<Cow<'a, S>>, |
| 178 | <S as ToOwned>::Owned: fmt::Debug, |
| 179 | { |
| 180 | AnsiGenericString { |
| 181 | string: input.into(), |
| 182 | style: self.normal(), |
| 183 | } |
| 184 | } |
| 185 | } |
| 186 | |
| 187 | // ---- writers for individual ANSI strings ---- |
| 188 | |
| 189 | impl<'a> fmt::Display for AnsiString<'a> { |
| 190 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| 191 | let w: &mut dyn fmt::Write = f; |
| 192 | self.write_to_any(w) |
| 193 | } |
| 194 | } |
| 195 | |
| 196 | impl<'a> AnsiByteString<'a> { |
| 197 | /// Write an `AnsiByteString` to an `io::Write`. This writes the escape |
| 198 | /// sequences for the associated `Style` around the bytes. |
| 199 | pub fn write_to<W: io::Write>(&self, w: &mut W) -> io::Result<()> { |
| 200 | let w: &mut dyn io::Write = w; |
| 201 | self.write_to_any(w) |
| 202 | } |
| 203 | } |
| 204 | |
| 205 | impl<'a, S: 'a + ToOwned + ?Sized> AnsiGenericString<'a, S> |
| 206 | where |
| 207 | <S as ToOwned>::Owned: fmt::Debug, |
| 208 | &'a S: AsRef<[u8]>, |
| 209 | { |
| 210 | fn write_to_any<W: AnyWrite<Wstr = S> + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> { |
| 211 | write!(w, " {}" , self.style.prefix())?; |
| 212 | w.write_str(self.string.as_ref())?; |
| 213 | write!(w, " {}" , self.style.suffix()) |
| 214 | } |
| 215 | } |
| 216 | |
| 217 | // ---- writers for combined ANSI strings ---- |
| 218 | |
| 219 | impl<'a> fmt::Display for AnsiStrings<'a> { |
| 220 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| 221 | let f: &mut dyn fmt::Write = f; |
| 222 | self.write_to_any(f) |
| 223 | } |
| 224 | } |
| 225 | |
| 226 | impl<'a> AnsiByteStrings<'a> { |
| 227 | /// Write `AnsiByteStrings` to an `io::Write`. This writes the minimal |
| 228 | /// escape sequences for the associated `Style`s around each set of |
| 229 | /// bytes. |
| 230 | pub fn write_to<W: io::Write>(&self, w: &mut W) -> io::Result<()> { |
| 231 | let w: &mut dyn io::Write = w; |
| 232 | self.write_to_any(w) |
| 233 | } |
| 234 | } |
| 235 | |
| 236 | impl<'a, S: 'a + ToOwned + ?Sized + PartialEq> AnsiGenericStrings<'a, S> |
| 237 | where |
| 238 | <S as ToOwned>::Owned: fmt::Debug, |
| 239 | &'a S: AsRef<[u8]>, |
| 240 | { |
| 241 | fn write_to_any<W: AnyWrite<Wstr = S> + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> { |
| 242 | use self::Difference::*; |
| 243 | |
| 244 | let first = match self.0.first() { |
| 245 | None => return Ok(()), |
| 246 | Some(f) => f, |
| 247 | }; |
| 248 | |
| 249 | write!(w, " {}" , first.style.prefix())?; |
| 250 | w.write_str(first.string.as_ref())?; |
| 251 | |
| 252 | for window in self.0.windows(2) { |
| 253 | match Difference::between(&window[0].style, &window[1].style) { |
| 254 | ExtraStyles(style) => write!(w, " {}" , style.prefix())?, |
| 255 | Reset => write!(w, " {}{}" , RESET, window[1].style.prefix())?, |
| 256 | Empty => { /* Do nothing! */ } |
| 257 | } |
| 258 | |
| 259 | w.write_str(&window[1].string)?; |
| 260 | } |
| 261 | |
| 262 | // Write the final reset string after all of the AnsiStrings have been |
| 263 | // written, *except* if the last one has no styles, because it would |
| 264 | // have already been written by this point. |
| 265 | if let Some(last) = self.0.last() { |
| 266 | if !last.style.is_plain() { |
| 267 | write!(w, " {}" , RESET)?; |
| 268 | } |
| 269 | } |
| 270 | |
| 271 | Ok(()) |
| 272 | } |
| 273 | } |
| 274 | |
| 275 | // ---- tests ---- |
| 276 | |
| 277 | #[cfg (test)] |
| 278 | mod tests { |
| 279 | pub use super::super::AnsiStrings; |
| 280 | pub use crate::style::Color::*; |
| 281 | pub use crate::style::Style; |
| 282 | |
| 283 | #[test ] |
| 284 | fn no_control_codes_for_plain() { |
| 285 | let one = Style::default().paint("one" ); |
| 286 | let two = Style::default().paint("two" ); |
| 287 | let output = AnsiStrings(&[one, two]).to_string(); |
| 288 | assert_eq!(output, "onetwo" ); |
| 289 | } |
| 290 | } |
| 291 | |