| 1 | //! # Style
|
| 2 | //!
|
| 3 | //! The `style` module provides a functionality to apply attributes and colors on your text.
|
| 4 | //!
|
| 5 | //! This documentation does not contain a lot of examples. The reason is that it's fairly
|
| 6 | //! obvious how to use this crate. Although, we do provide
|
| 7 | //! [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) repository
|
| 8 | //! to demonstrate the capabilities.
|
| 9 | //!
|
| 10 | //! ## Platform-specific Notes
|
| 11 | //!
|
| 12 | //! Not all features are supported on all terminals/platforms. You should always consult
|
| 13 | //! platform-specific notes of the following types:
|
| 14 | //!
|
| 15 | //! * [Color](enum.Color.html#platform-specific-notes)
|
| 16 | //! * [Attribute](enum.Attribute.html#platform-specific-notes)
|
| 17 | //!
|
| 18 | //! ## Examples
|
| 19 | //!
|
| 20 | //! A few examples of how to use the style module.
|
| 21 | //!
|
| 22 | //! ### Colors
|
| 23 | //!
|
| 24 | //! How to change the terminal text color.
|
| 25 | //!
|
| 26 | //! Command API:
|
| 27 | //!
|
| 28 | //! Using the Command API to color text.
|
| 29 | //!
|
| 30 | //! ```no_run
|
| 31 | //! use std::io::{self, Write};
|
| 32 | //! use crossterm::execute;
|
| 33 | //! use crossterm::style::{Print, SetForegroundColor, SetBackgroundColor, ResetColor, Color, Attribute};
|
| 34 | //!
|
| 35 | //! fn main() -> io::Result<()> {
|
| 36 | //! execute!(
|
| 37 | //! io::stdout(),
|
| 38 | //! // Blue foreground
|
| 39 | //! SetForegroundColor(Color::Blue),
|
| 40 | //! // Red background
|
| 41 | //! SetBackgroundColor(Color::Red),
|
| 42 | //! // Print text
|
| 43 | //! Print("Blue text on Red." .to_string()),
|
| 44 | //! // Reset to default colors
|
| 45 | //! ResetColor
|
| 46 | //! )
|
| 47 | //! }
|
| 48 | //! ```
|
| 49 | //!
|
| 50 | //! Functions:
|
| 51 | //!
|
| 52 | //! Using functions from [`Stylize`](crate::style::Stylize) on a `String` or `&'static str` to color
|
| 53 | //! it.
|
| 54 | //!
|
| 55 | //! ```no_run
|
| 56 | //! use crossterm::style::Stylize;
|
| 57 | //!
|
| 58 | //! println!("{}" , "Red foreground color & blue background." .red().on_blue());
|
| 59 | //! ```
|
| 60 | //!
|
| 61 | //! ### Attributes
|
| 62 | //!
|
| 63 | //! How to apply terminal attributes to text.
|
| 64 | //!
|
| 65 | //! Command API:
|
| 66 | //!
|
| 67 | //! Using the Command API to set attributes.
|
| 68 | //!
|
| 69 | //! ```no_run
|
| 70 | //! use std::io::{self, Write};
|
| 71 | //!
|
| 72 | //! use crossterm::execute;
|
| 73 | //! use crossterm::style::{Attribute, Print, SetAttribute};
|
| 74 | //!
|
| 75 | //! fn main() -> io::Result<()> {
|
| 76 | //! execute!(
|
| 77 | //! io::stdout(),
|
| 78 | //! // Set to bold
|
| 79 | //! SetAttribute(Attribute::Bold),
|
| 80 | //! Print("Bold text here." .to_string()),
|
| 81 | //! // Reset all attributes
|
| 82 | //! SetAttribute(Attribute::Reset)
|
| 83 | //! )
|
| 84 | //! }
|
| 85 | //! ```
|
| 86 | //!
|
| 87 | //! Functions:
|
| 88 | //!
|
| 89 | //! Using [`Stylize`](crate::style::Stylize) functions on a `String` or `&'static str` to set
|
| 90 | //! attributes to it.
|
| 91 | //!
|
| 92 | //! ```no_run
|
| 93 | //! use crossterm::style::Stylize;
|
| 94 | //!
|
| 95 | //! println!("{}" , "Bold" .bold());
|
| 96 | //! println!("{}" , "Underlined" .underlined());
|
| 97 | //! println!("{}" , "Negative" .negative());
|
| 98 | //! ```
|
| 99 | //!
|
| 100 | //! Displayable:
|
| 101 | //!
|
| 102 | //! [`Attribute`](enum.Attribute.html) implements [Display](https://doc.rust-lang.org/beta/std/fmt/trait.Display.html) and therefore it can be formatted like:
|
| 103 | //!
|
| 104 | //! ```no_run
|
| 105 | //! use crossterm::style::Attribute;
|
| 106 | //!
|
| 107 | //! println!(
|
| 108 | //! "{} Underlined {} No Underline" ,
|
| 109 | //! Attribute::Underlined,
|
| 110 | //! Attribute::NoUnderline
|
| 111 | //! );
|
| 112 | //! ```
|
| 113 |
|
| 114 | use std::{
|
| 115 | env,
|
| 116 | fmt::{self, Display},
|
| 117 | };
|
| 118 |
|
| 119 | use crate::command::execute_fmt;
|
| 120 | use crate::{csi, impl_display, Command};
|
| 121 |
|
| 122 | pub use self::{
|
| 123 | attributes::Attributes,
|
| 124 | content_style::ContentStyle,
|
| 125 | styled_content::StyledContent,
|
| 126 | stylize::Stylize,
|
| 127 | types::{Attribute, Color, Colored, Colors},
|
| 128 | };
|
| 129 |
|
| 130 | mod attributes;
|
| 131 | mod content_style;
|
| 132 | mod styled_content;
|
| 133 | mod stylize;
|
| 134 | mod sys;
|
| 135 | mod types;
|
| 136 |
|
| 137 | /// Creates a `StyledContent`.
|
| 138 | ///
|
| 139 | /// This could be used to style any type that implements `Display` with colors and text attributes.
|
| 140 | ///
|
| 141 | /// See [`StyledContent`](struct.StyledContent.html) for more info.
|
| 142 | ///
|
| 143 | /// # Examples
|
| 144 | ///
|
| 145 | /// ```no_run
|
| 146 | /// use crossterm::style::{style, Stylize, Color};
|
| 147 | ///
|
| 148 | /// let styled_content = style("Blue colored text on yellow background" )
|
| 149 | /// .with(Color::Blue)
|
| 150 | /// .on(Color::Yellow);
|
| 151 | ///
|
| 152 | /// println!("{}" , styled_content);
|
| 153 | /// ```
|
| 154 | pub fn style<D: Display>(val: D) -> StyledContent<D> {
|
| 155 | ContentStyle::new().apply(val)
|
| 156 | }
|
| 157 |
|
| 158 | /// Returns available color count.
|
| 159 | ///
|
| 160 | /// # Notes
|
| 161 | ///
|
| 162 | /// This does not always provide a good result.
|
| 163 | pub fn available_color_count() -> u16 {
|
| 164 | #[cfg (windows)]
|
| 165 | {
|
| 166 | // Check if we're running in a pseudo TTY, which supports true color.
|
| 167 | // Fall back to env vars otherwise for other terminals on Windows.
|
| 168 | if crate::ansi_support::supports_ansi() {
|
| 169 | return u16::MAX;
|
| 170 | }
|
| 171 | }
|
| 172 |
|
| 173 | const DEFAULT: u16 = 8;
|
| 174 | env::var("COLORTERM" )
|
| 175 | .or_else(|_| env::var("TERM" ))
|
| 176 | .map_or(DEFAULT, |x: String| match x {
|
| 177 | _ if x.contains("24bit" ) || x.contains("truecolor" ) => u16::MAX,
|
| 178 | _ if x.contains("256" ) => 256,
|
| 179 | _ => DEFAULT,
|
| 180 | })
|
| 181 | }
|
| 182 |
|
| 183 | /// Forces colored output on or off globally, overriding NO_COLOR.
|
| 184 | ///
|
| 185 | /// # Notes
|
| 186 | ///
|
| 187 | /// crossterm supports NO_COLOR (<https://no-color.org/>) to disabled colored output.
|
| 188 | ///
|
| 189 | /// This API allows applications to override that behavior and force colorized output
|
| 190 | /// even if NO_COLOR is set.
|
| 191 | pub fn force_color_output(enabled: bool) {
|
| 192 | Colored::set_ansi_color_disabled(!enabled)
|
| 193 | }
|
| 194 |
|
| 195 | /// A command that sets the the foreground color.
|
| 196 | ///
|
| 197 | /// See [`Color`](enum.Color.html) for more info.
|
| 198 | ///
|
| 199 | /// [`SetColors`](struct.SetColors.html) can also be used to set both the foreground and background
|
| 200 | /// color in one command.
|
| 201 | ///
|
| 202 | /// # Notes
|
| 203 | ///
|
| 204 | /// Commands must be executed/queued for execution otherwise they do nothing.
|
| 205 | #[derive (Debug, Clone, Copy, PartialEq, Eq)]
|
| 206 | pub struct SetForegroundColor(pub Color);
|
| 207 |
|
| 208 | impl Command for SetForegroundColor {
|
| 209 | fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
|
| 210 | write!(f, csi!("{}m" ), Colored::ForegroundColor(self.0))
|
| 211 | }
|
| 212 |
|
| 213 | #[cfg (windows)]
|
| 214 | fn execute_winapi(&self) -> std::io::Result<()> {
|
| 215 | sys::windows::set_foreground_color(self.0)
|
| 216 | }
|
| 217 | }
|
| 218 |
|
| 219 | /// A command that sets the the background color.
|
| 220 | ///
|
| 221 | /// See [`Color`](enum.Color.html) for more info.
|
| 222 | ///
|
| 223 | /// [`SetColors`](struct.SetColors.html) can also be used to set both the foreground and background
|
| 224 | /// color with one command.
|
| 225 | ///
|
| 226 | /// # Notes
|
| 227 | ///
|
| 228 | /// Commands must be executed/queued for execution otherwise they do nothing.
|
| 229 | #[derive (Debug, Clone, Copy, PartialEq, Eq)]
|
| 230 | pub struct SetBackgroundColor(pub Color);
|
| 231 |
|
| 232 | impl Command for SetBackgroundColor {
|
| 233 | fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
|
| 234 | write!(f, csi!("{}m" ), Colored::BackgroundColor(self.0))
|
| 235 | }
|
| 236 |
|
| 237 | #[cfg (windows)]
|
| 238 | fn execute_winapi(&self) -> std::io::Result<()> {
|
| 239 | sys::windows::set_background_color(self.0)
|
| 240 | }
|
| 241 | }
|
| 242 |
|
| 243 | /// A command that sets the the underline color.
|
| 244 | ///
|
| 245 | /// See [`Color`](enum.Color.html) for more info.
|
| 246 | ///
|
| 247 | /// [`SetColors`](struct.SetColors.html) can also be used to set both the foreground and background
|
| 248 | /// color with one command.
|
| 249 | ///
|
| 250 | /// # Notes
|
| 251 | ///
|
| 252 | /// Commands must be executed/queued for execution otherwise they do nothing.
|
| 253 | #[derive (Debug, Clone, Copy, PartialEq, Eq)]
|
| 254 | pub struct SetUnderlineColor(pub Color);
|
| 255 |
|
| 256 | impl Command for SetUnderlineColor {
|
| 257 | fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
|
| 258 | write!(f, csi!("{}m" ), Colored::UnderlineColor(self.0))
|
| 259 | }
|
| 260 |
|
| 261 | #[cfg (windows)]
|
| 262 | fn execute_winapi(&self) -> std::io::Result<()> {
|
| 263 | Err(std::io::Error::new(
|
| 264 | std::io::ErrorKind::Other,
|
| 265 | "SetUnderlineColor not supported by winapi." ,
|
| 266 | ))
|
| 267 | }
|
| 268 | }
|
| 269 |
|
| 270 | /// A command that optionally sets the foreground and/or background color.
|
| 271 | ///
|
| 272 | /// For example:
|
| 273 | /// ```no_run
|
| 274 | /// use std::io::{stdout, Write};
|
| 275 | ///
|
| 276 | /// use crossterm::execute;
|
| 277 | /// use crossterm::style::{Color::{Green, Black}, Colors, Print, SetColors};
|
| 278 | ///
|
| 279 | /// execute!(
|
| 280 | /// stdout(),
|
| 281 | /// SetColors(Colors::new(Green, Black)),
|
| 282 | /// Print("Hello, world!" .to_string()),
|
| 283 | /// ).unwrap();
|
| 284 | /// ```
|
| 285 | ///
|
| 286 | /// See [`Colors`](struct.Colors.html) for more info.
|
| 287 | ///
|
| 288 | /// # Notes
|
| 289 | ///
|
| 290 | /// Commands must be executed/queued for execution otherwise they do nothing.
|
| 291 | #[derive (Debug, Clone, Copy, PartialEq, Eq)]
|
| 292 | pub struct SetColors(pub Colors);
|
| 293 |
|
| 294 | impl Command for SetColors {
|
| 295 | fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
|
| 296 | // Writing both foreground and background colors in one command resulted in about 20% more
|
| 297 | // FPS (20 to 24 fps) on a fullscreen (171x51) app that writes every cell with a different
|
| 298 | // foreground and background color, compared to separately using the SetForegroundColor and
|
| 299 | // SetBackgroundColor commands (iTerm2, M2 Macbook Pro). `Esc[38;5;<fg>mEsc[48;5;<bg>m` (16
|
| 300 | // chars) vs `Esc[38;5;<fg>;48;5;<bg>m` (14 chars)
|
| 301 | match (self.0.foreground, self.0.background) {
|
| 302 | (Some(fg), Some(bg)) => {
|
| 303 | write!(
|
| 304 | f,
|
| 305 | csi!("{};{}m" ),
|
| 306 | Colored::ForegroundColor(fg),
|
| 307 | Colored::BackgroundColor(bg)
|
| 308 | )
|
| 309 | }
|
| 310 | (Some(fg), None) => write!(f, csi!("{}m" ), Colored::ForegroundColor(fg)),
|
| 311 | (None, Some(bg)) => write!(f, csi!("{}m" ), Colored::BackgroundColor(bg)),
|
| 312 | (None, None) => Ok(()),
|
| 313 | }
|
| 314 | }
|
| 315 |
|
| 316 | #[cfg (windows)]
|
| 317 | fn execute_winapi(&self) -> std::io::Result<()> {
|
| 318 | if let Some(color) = self.0.foreground {
|
| 319 | sys::windows::set_foreground_color(color)?;
|
| 320 | }
|
| 321 | if let Some(color) = self.0.background {
|
| 322 | sys::windows::set_background_color(color)?;
|
| 323 | }
|
| 324 | Ok(())
|
| 325 | }
|
| 326 | }
|
| 327 |
|
| 328 | /// A command that sets an attribute.
|
| 329 | ///
|
| 330 | /// See [`Attribute`](enum.Attribute.html) for more info.
|
| 331 | ///
|
| 332 | /// # Notes
|
| 333 | ///
|
| 334 | /// Commands must be executed/queued for execution otherwise they do nothing.
|
| 335 | #[derive (Debug, Clone, Copy, PartialEq, Eq)]
|
| 336 | pub struct SetAttribute(pub Attribute);
|
| 337 |
|
| 338 | impl Command for SetAttribute {
|
| 339 | fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
|
| 340 | write!(f, csi!("{}m" ), self.0.sgr())
|
| 341 | }
|
| 342 |
|
| 343 | #[cfg (windows)]
|
| 344 | fn execute_winapi(&self) -> std::io::Result<()> {
|
| 345 | // attributes are not supported by WinAPI.
|
| 346 | Ok(())
|
| 347 | }
|
| 348 | }
|
| 349 |
|
| 350 | /// A command that sets several attributes.
|
| 351 | ///
|
| 352 | /// See [`Attributes`](struct.Attributes.html) for more info.
|
| 353 | ///
|
| 354 | /// # Notes
|
| 355 | ///
|
| 356 | /// Commands must be executed/queued for execution otherwise they do nothing.
|
| 357 | #[derive (Debug, Clone, Copy, PartialEq, Eq)]
|
| 358 | pub struct SetAttributes(pub Attributes);
|
| 359 |
|
| 360 | impl Command for SetAttributes {
|
| 361 | fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
|
| 362 | for attr: Attribute in Attribute::iterator() {
|
| 363 | if self.0.has(attribute:attr) {
|
| 364 | SetAttribute(attr).write_ansi(f)?;
|
| 365 | }
|
| 366 | }
|
| 367 | Ok(())
|
| 368 | }
|
| 369 |
|
| 370 | #[cfg (windows)]
|
| 371 | fn execute_winapi(&self) -> std::io::Result<()> {
|
| 372 | // attributes are not supported by WinAPI.
|
| 373 | Ok(())
|
| 374 | }
|
| 375 | }
|
| 376 |
|
| 377 | /// A command that sets a style (colors and attributes).
|
| 378 | ///
|
| 379 | /// # Notes
|
| 380 | ///
|
| 381 | /// Commands must be executed/queued for execution otherwise they do nothing.
|
| 382 | #[derive (Debug, Clone, Copy, PartialEq, Eq)]
|
| 383 | pub struct SetStyle(pub ContentStyle);
|
| 384 |
|
| 385 | impl Command for SetStyle {
|
| 386 | fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
|
| 387 | if let Some(bg) = self.0.background_color {
|
| 388 | execute_fmt(f, SetBackgroundColor(bg)).map_err(|_| fmt::Error)?;
|
| 389 | }
|
| 390 | if let Some(fg) = self.0.foreground_color {
|
| 391 | execute_fmt(f, SetForegroundColor(fg)).map_err(|_| fmt::Error)?;
|
| 392 | }
|
| 393 | if let Some(ul) = self.0.underline_color {
|
| 394 | execute_fmt(f, SetUnderlineColor(ul)).map_err(|_| fmt::Error)?;
|
| 395 | }
|
| 396 | if !self.0.attributes.is_empty() {
|
| 397 | execute_fmt(f, SetAttributes(self.0.attributes)).map_err(|_| fmt::Error)?;
|
| 398 | }
|
| 399 |
|
| 400 | Ok(())
|
| 401 | }
|
| 402 |
|
| 403 | #[cfg (windows)]
|
| 404 | fn execute_winapi(&self) -> std::io::Result<()> {
|
| 405 | panic!("tried to execute SetStyle command using WinAPI, use ANSI instead" );
|
| 406 | }
|
| 407 |
|
| 408 | #[cfg (windows)]
|
| 409 | fn is_ansi_code_supported(&self) -> bool {
|
| 410 | true
|
| 411 | }
|
| 412 | }
|
| 413 |
|
| 414 | /// A command that prints styled content.
|
| 415 | ///
|
| 416 | /// See [`StyledContent`](struct.StyledContent.html) for more info.
|
| 417 | ///
|
| 418 | /// # Notes
|
| 419 | ///
|
| 420 | /// Commands must be executed/queued for execution otherwise they do nothing.
|
| 421 | #[derive (Debug, Copy, Clone)]
|
| 422 | pub struct PrintStyledContent<D: Display>(pub StyledContent<D>);
|
| 423 |
|
| 424 | impl<D: Display> Command for PrintStyledContent<D> {
|
| 425 | fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
|
| 426 | let style = self.0.style();
|
| 427 |
|
| 428 | let mut reset_background = false;
|
| 429 | let mut reset_foreground = false;
|
| 430 | let mut reset = false;
|
| 431 |
|
| 432 | if let Some(bg) = style.background_color {
|
| 433 | execute_fmt(f, SetBackgroundColor(bg)).map_err(|_| fmt::Error)?;
|
| 434 | reset_background = true;
|
| 435 | }
|
| 436 | if let Some(fg) = style.foreground_color {
|
| 437 | execute_fmt(f, SetForegroundColor(fg)).map_err(|_| fmt::Error)?;
|
| 438 | reset_foreground = true;
|
| 439 | }
|
| 440 | if let Some(ul) = style.underline_color {
|
| 441 | execute_fmt(f, SetUnderlineColor(ul)).map_err(|_| fmt::Error)?;
|
| 442 | reset_foreground = true;
|
| 443 | }
|
| 444 |
|
| 445 | if !style.attributes.is_empty() {
|
| 446 | execute_fmt(f, SetAttributes(style.attributes)).map_err(|_| fmt::Error)?;
|
| 447 | reset = true;
|
| 448 | }
|
| 449 |
|
| 450 | write!(f, " {}" , self.0.content())?;
|
| 451 |
|
| 452 | if reset {
|
| 453 | // NOTE: This will reset colors even though self has no colors, hence produce unexpected
|
| 454 | // resets.
|
| 455 | // TODO: reset the set attributes only.
|
| 456 | execute_fmt(f, ResetColor).map_err(|_| fmt::Error)?;
|
| 457 | } else {
|
| 458 | // NOTE: Since the above bug, we do not need to reset colors when we reset attributes.
|
| 459 | if reset_background {
|
| 460 | execute_fmt(f, SetBackgroundColor(Color::Reset)).map_err(|_| fmt::Error)?;
|
| 461 | }
|
| 462 | if reset_foreground {
|
| 463 | execute_fmt(f, SetForegroundColor(Color::Reset)).map_err(|_| fmt::Error)?;
|
| 464 | }
|
| 465 | }
|
| 466 |
|
| 467 | Ok(())
|
| 468 | }
|
| 469 |
|
| 470 | #[cfg (windows)]
|
| 471 | fn execute_winapi(&self) -> std::io::Result<()> {
|
| 472 | Ok(())
|
| 473 | }
|
| 474 | }
|
| 475 |
|
| 476 | /// A command that resets the colors back to default.
|
| 477 | ///
|
| 478 | /// # Notes
|
| 479 | ///
|
| 480 | /// Commands must be executed/queued for execution otherwise they do nothing.
|
| 481 | #[derive (Debug, Clone, Copy, PartialEq, Eq)]
|
| 482 | pub struct ResetColor;
|
| 483 |
|
| 484 | impl Command for ResetColor {
|
| 485 | fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
|
| 486 | f.write_str(csi!("0m" ))
|
| 487 | }
|
| 488 |
|
| 489 | #[cfg (windows)]
|
| 490 | fn execute_winapi(&self) -> std::io::Result<()> {
|
| 491 | sys::windows::reset()
|
| 492 | }
|
| 493 | }
|
| 494 |
|
| 495 | /// A command that prints the given displayable type.
|
| 496 | ///
|
| 497 | /// Commands must be executed/queued for execution otherwise they do nothing.
|
| 498 | #[derive (Debug, Clone, Copy, PartialEq, Eq)]
|
| 499 | pub struct Print<T: Display>(pub T);
|
| 500 |
|
| 501 | impl<T: Display> Command for Print<T> {
|
| 502 | fn write_ansi(&self, f: &mut impl fmt::Write) -> fmt::Result {
|
| 503 | write!(f, " {}" , self.0)
|
| 504 | }
|
| 505 |
|
| 506 | #[cfg (windows)]
|
| 507 | fn execute_winapi(&self) -> std::io::Result<()> {
|
| 508 | panic!("tried to execute Print command using WinAPI, use ANSI instead" );
|
| 509 | }
|
| 510 |
|
| 511 | #[cfg (windows)]
|
| 512 | fn is_ansi_code_supported(&self) -> bool {
|
| 513 | true
|
| 514 | }
|
| 515 | }
|
| 516 |
|
| 517 | impl<T: Display> Display for Print<T> {
|
| 518 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
| 519 | self.0.fmt(f)
|
| 520 | }
|
| 521 | }
|
| 522 |
|
| 523 | impl_display!(for SetForegroundColor);
|
| 524 | impl_display!(for SetBackgroundColor);
|
| 525 | impl_display!(for SetColors);
|
| 526 | impl_display!(for SetAttribute);
|
| 527 | impl_display!(for PrintStyledContent<String>);
|
| 528 | impl_display!(for PrintStyledContent<&'static str>);
|
| 529 | impl_display!(for ResetColor);
|
| 530 |
|
| 531 | /// Utility function for ANSI parsing in Color and Colored.
|
| 532 | /// Gets the next element of `iter` and tries to parse it as a `u8`.
|
| 533 | fn parse_next_u8<'a>(iter: &mut impl Iterator<Item = &'a str>) -> Option<u8> {
|
| 534 | iter.next().and_then(|s: &'a str| s.parse().ok())
|
| 535 | }
|
| 536 |
|
| 537 | #[cfg (test)]
|
| 538 | mod tests {
|
| 539 | use super::*;
|
| 540 |
|
| 541 | // On Windows many env var tests will fail so we need to conditionally check for ANSI support.
|
| 542 | // This allows other terminals on Windows to still assert env var support.
|
| 543 | macro_rules! skip_windows_ansi_supported {
|
| 544 | () => {
|
| 545 | #[cfg(windows)]
|
| 546 | {
|
| 547 | if crate::ansi_support::supports_ansi() {
|
| 548 | return;
|
| 549 | }
|
| 550 | }
|
| 551 | };
|
| 552 | }
|
| 553 |
|
| 554 | #[cfg_attr (windows, test)]
|
| 555 | #[cfg (windows)]
|
| 556 | fn windows_always_truecolor() {
|
| 557 | // This should always be true on supported Windows 10+,
|
| 558 | // but downlevel Windows clients and other terminals may fail `cargo test` otherwise.
|
| 559 | if crate::ansi_support::supports_ansi() {
|
| 560 | assert_eq!(u16::MAX, available_color_count());
|
| 561 | };
|
| 562 | }
|
| 563 |
|
| 564 | #[test ]
|
| 565 | fn colorterm_overrides_term() {
|
| 566 | skip_windows_ansi_supported!();
|
| 567 | temp_env::with_vars(
|
| 568 | [
|
| 569 | ("COLORTERM" , Some("truecolor" )),
|
| 570 | ("TERM" , Some("xterm-256color" )),
|
| 571 | ],
|
| 572 | || {
|
| 573 | assert_eq!(u16::MAX, available_color_count());
|
| 574 | },
|
| 575 | );
|
| 576 | }
|
| 577 |
|
| 578 | #[test ]
|
| 579 | fn term_24bits() {
|
| 580 | skip_windows_ansi_supported!();
|
| 581 | temp_env::with_vars(
|
| 582 | [("COLORTERM" , None), ("TERM" , Some("xterm-24bits" ))],
|
| 583 | || {
|
| 584 | assert_eq!(u16::MAX, available_color_count());
|
| 585 | },
|
| 586 | );
|
| 587 | }
|
| 588 |
|
| 589 | #[test ]
|
| 590 | fn term_256color() {
|
| 591 | skip_windows_ansi_supported!();
|
| 592 | temp_env::with_vars(
|
| 593 | [("COLORTERM" , None), ("TERM" , Some("xterm-256color" ))],
|
| 594 | || {
|
| 595 | assert_eq!(256u16, available_color_count());
|
| 596 | },
|
| 597 | );
|
| 598 | }
|
| 599 |
|
| 600 | #[test ]
|
| 601 | fn default_color_count() {
|
| 602 | skip_windows_ansi_supported!();
|
| 603 | temp_env::with_vars([("COLORTERM" , None::<&str>), ("TERM" , None)], || {
|
| 604 | assert_eq!(8, available_color_count());
|
| 605 | });
|
| 606 | }
|
| 607 |
|
| 608 | #[test ]
|
| 609 | fn unsupported_term_colorterm_values() {
|
| 610 | skip_windows_ansi_supported!();
|
| 611 | temp_env::with_vars(
|
| 612 | [
|
| 613 | ("COLORTERM" , Some("gibberish" )),
|
| 614 | ("TERM" , Some("gibberish" )),
|
| 615 | ],
|
| 616 | || {
|
| 617 | assert_eq!(8u16, available_color_count());
|
| 618 | },
|
| 619 | );
|
| 620 | }
|
| 621 | }
|
| 622 | |