feat: add display UI with splash screen, dashboard, and RSSI sparkline

Implement full SSD1306 OLED display support:
- Splash screen on boot (1.5s) with project name, version, board
- Dashboard showing radio state, config, counters, RSSI/SNR
- RSSI history sparkline (64-sample ring buffer, 2px-wide bars)
- DisplayOn/DisplayOff host commands routed via dedicated channel
- Pure render functions against generic DrawTarget (no hardware coupling)
- Display on/off control bypasses radio task entirely

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
swaitsandClaude Opus 4.6 committed 2026-03-29 20:42:54 -06:00
1 parent 7064110293
commit 3102fc747a
17624 files changed
+4564928 -38

No files matched your search

+1
View File
@@ -0,0 +1 @@
{"files":{".cargo_vcs_info.json":"cff68e918756520115b4de8efd289e777f8e8c140945a8ad41ded7d03f1e77ef","CHANGELOG.md":"46f7a8431251a85fd247af77ceae18f6ba2e2cba4f9532038ab8996e95a262bd","Cargo.lock":"69d25b3cc1b1e30a509280a70dab057db92f43593d6eab0b82fa10538b21f158","Cargo.toml":"9ddc320925bb19bf76573dd0569a9649c6043bf713c7b1ff2d5830a91d908919","Cargo.toml.orig":"8587d4bbff519cc390f8de40c479781086efcc50920e41a95b1c5f7b42ff61f7","LICENSE":"267afe8db5697b4b668d36a5a0d481b67d16ae751465e9ddf03d1e77a79c140a","README.md":"73251dc293e88ed1a214a9b1d48a497440eb0c7b13c2656f4b20d0f13ab623a6","build.rs":"afffaf4540849a8daf1e5a2efbb7eba25ede31c77060ca4f14dc43f8b7aadbeb","src/colors.rs":"77aa1ea493d09575d833871aa2cddf6ea38657922b08e9cf7c8b81e3c86b4a26","src/colors/css.rs":"1084264e06e4ee48990cba93eb49468bbb4e92102cdc17c7c4df198fcd8241a2","src/colors/custom.rs":"21dd12c2caed2631872b2966ca54deac00c9222bc97edb11565d87b3e6f20c21","src/colors/dynamic.rs":"c8176d0659c3936eec170d07fda7a88dcfaf74778136090588405d5d77d164f0","src/colors/xterm.rs":"59ab4ab69c89d6b50489d2c9c73bc18c1ee32f9e7eb7e4b0f726b5301c4f348c","src/combo.rs":"75fec07f62765b8ce983b910d82a6aa7a2cb4d8f85bc343974e273d6fd049de6","src/dyn_colors.rs":"8802ba7e7c4a00a1fc51179b80f5744c33cf6c9c6e5be767fa2a0d4866029118","src/dyn_styles.rs":"a3947fd365f83317bc875710a2ee0e791d97df25e0bf7ae2f86683cf6e988c57","src/lib.rs":"68fcd1a81b4d383d696a914e2fa54a4268f1218de67ccb00e1dabea3f5be529a","src/overrides.rs":"b697e757ad1ec9a23645b3c269f3faea8c1e76c8eea8ce522f04a4ac104c05f4","src/styled_list.rs":"83c9cf37463978fc896aac0efc0cdd6f95b41e2d3f4ddda0a745e00a5ac75ba4","src/styles.rs":"081a08aacc1cbeeafeb8ea9097ef01b2501324889ea1a355887ee69f5b4c1491","src/supports_colors.rs":"1bb628e711086f3753b76b969a0234d134f4344d5c8b7feceb39e53b74ff35d3","src/tests.rs":"3202b4d9ace81e00e268c994876265ea546193693dc77559c697ded281b7ca6d"},"package":"d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d"}
+6
View File
@@ -0,0 +1,6 @@
{
"git": {
"sha1": "baf10f9a74007501379afdc002394a15a787dc59"
},
"path_in_vcs": ""
}
+74
View File
@@ -0,0 +1,74 @@
# Changelog
## [4.3.0] - 2026-02-22
### Fixed
- Scripts in the `scripts/` directory are no longer published in the crate package. Thanks [weiznich](https://github.com/owo-colors/owo-colors/pull/152) for your first contribution!
### Changed
- Mark methods with `#[rust_analyzer::completions(ignore_flyimport)]` and the `OwoColorize` trait with `#[rust_analyzer::completions(ignore_flyimport_methods)]`. This prevents owo-colors methods from being completed with rust-analyzer unless the `OwoColorize` trait is included.
Unfortunately, this also breaks explicit autocomplete commands such as Ctrl-Space in many editors. (The language server protocol doesn't appear to have a way to differentiate between implicit and explicit autocomplete commands.) On balance we believe this is the right approach, but please do provide feedback on [PR #141](https://github.com/owo-colors/owo-colors/pull/141) if it negatively affects you.
- Updated MSRV to Rust 1.81.
## [4.2.3] - 2025-09-29
### Fixed
Replaced obsolete `doc_auto_cfg` with `doc_cfg`, to fix Rust nightly builds with the `doc_cfg` flag enabled.
## [4.2.2] - 2025-06-23
### Fixed
Fixed applying a background color and a text effect (like underline or italic) at the same time ([#145]).
[#145]: https://github.com/owo-colors/owo-colors/issues/145
## [4.2.1] - 2025-05-15
### Fixed
- Fixed a couple of bugs while rendering custom colors ([#144]). Thanks [https://github.com/MiguelValentine](@MiguelValentine) for your first contribution!
[#144]: https://github.com/owo-colors/owo-colors/pull/144
## [4.2.0] - 2025-02-22
### Added
- `Style::prefix_formatter` and `Style::suffix_formatter` return `Display` formatters for the prefix and the suffix of a style, respectively.
- All the `*Display` types now have an `into_styled` function that converts those types into a `Styled`, erasing type parameters.
- Even more methods are now `const`.
### Changed
- The `Color` and `DynColor` traits are now explicitly marked sealed (i.e. downstream crates cannot implement them).
These traits were already effectively sealed due to a number of hidden methods that were not part of the API, but they are now explicitly so. In that sense this is not a breaking change, so it's being released under a new minor version rather than a major version.
## [4.1.1] - 2025-02-22
### Added
- The vast majority of owo-colors is now usable in const contexts.
### Fixed
- Documentation for `Stream` is now rendered properly. Thanks [purplesyringa](https://github.com/purplesyringa) for the contribution!
- Replace brittle const-promotion-based unsafe code with safe code. Thanks [Manish](https://github.com/Manishearth) for the contribution!
### Other
- owo-colors now lives under its own organization, https://github.com/owo-colors.
[4.3.0]: https://github.com/owo-colors/owo-colors/releases/tag/v4.3.0
[4.2.3]: https://github.com/owo-colors/owo-colors/releases/tag/v4.2.3
[4.2.2]: https://github.com/owo-colors/owo-colors/releases/tag/v4.2.2
[4.2.1]: https://github.com/owo-colors/owo-colors/releases/tag/v4.2.1
[4.2.0]: https://github.com/owo-colors/owo-colors/releases/tag/v4.2.0
[4.1.1]: https://github.com/owo-colors/owo-colors/releases/tag/v4.1.1
For information about earlier versions, see [the commit history](https://github.com/jam1garner/owo-colors/commits/master).
+74
View File
@@ -0,0 +1,74 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "hermit-abi"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
[[package]]
name = "is-terminal"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46"
dependencies = [
"hermit-abi",
"libc",
"windows-sys",
]
[[package]]
name = "is_ci"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45"
[[package]]
name = "libc"
version = "0.2.182"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
[[package]]
name = "owo-colors"
version = "4.3.0"
dependencies = [
"supports-color 2.1.0",
"supports-color 3.0.2",
]
[[package]]
name = "supports-color"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6398cde53adc3c4557306a96ce67b302968513830a77a95b2b17305d9719a89"
dependencies = [
"is-terminal",
"is_ci",
]
[[package]]
name = "supports-color"
version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c64fc7232dd8d2e4ac5ce4ef302b1d81e0b80d055b9d77c7c4f51f6aa4c867d6"
dependencies = [
"is_ci",
]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
+74
View File
@@ -0,0 +1,74 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
rust-version = "1.81"
name = "owo-colors"
version = "4.3.0"
authors = ["jam1garner <8260240+jam1garner@users.noreply.github.com>"]
build = "build.rs"
include = [
"Cargo.toml",
"build.rs",
"README.md",
"CHANGELOG.md",
"LICENSE",
"src/**/*.rs",
]
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Zero-allocation terminal colors that'll make people go owo"
documentation = "https://docs.rs/owo-colors"
readme = "README.md"
keywords = [
"cli",
"color",
"no-std",
"terminal",
"ansi_term",
]
categories = [
"command-line-interface",
"no-std::no-alloc",
]
license = "MIT"
repository = "https://github.com/owo-colors/owo-colors"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = [
"--cfg",
"doc_cfg",
]
[features]
alloc = []
supports-colors = [
"dep:supports-color-2",
"supports-color",
]
[lib]
name = "owo_colors"
path = "src/lib.rs"
[dependencies.supports-color]
version = "3.0.0"
optional = true
[dependencies.supports-color-2]
version = "2.0"
optional = true
package = "supports-color"
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 - present The owo-colors Developers
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+104
View File
@@ -0,0 +1,104 @@
# <img src="https://jam1.re/img/rust_owo.svg" height="100"> Colors
[![Current Crates.io Version](https://img.shields.io/crates/v/owo-colors.svg)](https://crates.io/crates/owo-colors)
[![docs-rs](https://docs.rs/owo-colors/badge.svg)](https://docs.rs/owo-colors)
![MSRV 1.81+](https://img.shields.io/badge/rustc-1.81+-blue.svg)
![Downloads](https://img.shields.io/crates/d/owo-colors)
A zero-allocation no_std-compatible zero-cost way to add color to your Rust
terminal to make people go owo.
**Supports:**
- [x] All std/core formatters
- [x] [Display](https://doc.rust-lang.org/std/fmt/trait.Display.html)
- [x] [Debug](https://doc.rust-lang.org/std/fmt/trait.Debug.html)
- [x] [Octal](https://doc.rust-lang.org/std/fmt/trait.Octal.html)
- [x] [LowerHex](https://doc.rust-lang.org/std/fmt/trait.LowerHex.html)
- [x] [UpperHex](https://doc.rust-lang.org/std/fmt/trait.UpperHex.html)
- [x] [Pointer](https://doc.rust-lang.org/std/fmt/trait.Pointer.html)
- [x] [Binary](https://doc.rust-lang.org/std/fmt/trait.Binary.html)
- [x] [LowerExp](https://doc.rust-lang.org/std/fmt/trait.LowerExp.html)
- [x] [UpperExp](https://doc.rust-lang.org/std/fmt/trait.UpperExp.html)
- [x] Optional checking for if a terminal supports colors
- [x] Enabled for CI
- [x] Disabled by default for non-terminal outputs
- [x] Overridable by `NO_COLOR`/`FORCE_COLOR` environment variables
- [x] Overridable programmatically via [`set_override`](https://docs.rs/owo-colors/latest/owo_colors/fn.set_override.html)
- [x] Dependency-less by default
- [x] 100% safe code
- [x] Most functions are `const`
- [x] Hand picked names for all ANSI (4-bit) and Xterm (8-bit) colors
- [x] Support for RGB colors
- [x] Set colors at compile time by generics or at runtime by value
- [x] All ANSI colors
- [x] Basic support (normal and bright variants)
- [x] Xterm support (high compatibility and 256 colors)
- [x] Truecolor support (modern, 48-bit color)
- [x] Styling (underline, strikethrough, etc)
owo-colors is also more-or-less a drop-in replacement for
[colored](https://crates.io/crates/colored), allowing colored to work in a
no_std environment. No allocations or dependencies required because embedded
systems deserve to be pretty too uwu.
To add to your Cargo.toml:
```toml
owo-colors = "4"
```
## Example
```rust
use owo_colors::OwoColorize;
fn main() {
// Foreground colors
println!("My number is {:#x}!", 10.green());
// Background colors
println!("My number is not {}!", 4.on_red());
}
```
## Generic colors
```rust
use owo_colors::OwoColorize;
use owo_colors::colors::*;
fn main() {
// Generically color
println!("My number might be {}!", 4.fg::<Black>().bg::<Yellow>());
}
```
## Stylize
```rust
use owo_colors::OwoColorize;
println!("{}", "strikethrough".strikethrough());
```
## Only Style on Supported Terminals
```rust
use owo_colors::{OwoColorize, Stream::Stdout};
println!(
"{}",
"colored blue if a supported terminal"
.if_supports_color(Stdout, |text| text.bright_blue())
);
```
Supports `NO_COLOR`/`FORCE_COLOR` environment variables, checks if it's a tty,
checks if it's running in CI (and thus likely supports color), and checks which
terminal is being used. (Note: requires `supports-colors` feature)
## Minimum supported Rust version (MSRV)
The MSRV of owo-colors is **Rust 1.81**.
This library has a conservative MSRV policy — Rust versions from the last 12 months will be supported (features available in newer versions will be gated by `build.rs`), the MSRV will only be bumped if required, and if the MSRV is bumped it will always occur with a new minor version.
+58
View File
@@ -0,0 +1,58 @@
use std::{env, process::Command, str};
fn main() {
// Once the MSRV is 1.74, we can replace this with a lint in Cargo.toml instead.
println!("cargo:rustc-check-cfg=cfg(doc_cfg)");
println!("cargo:rustc-check-cfg=cfg(const_mut_refs)");
// Rust 1.83+ support &mut in const fn.
if let Some(compiler) = rustc_version() {
if compiler.is_newer_than_minor(83) {
println!("cargo:rustc-cfg=const_mut_refs");
}
}
}
struct Compiler {
minor: u32,
channel: ReleaseChannel,
}
impl Compiler {
fn is_newer_than_minor(&self, minor: u32) -> bool {
match self.channel {
ReleaseChannel::Stable | ReleaseChannel::Beta => self.minor >= minor,
ReleaseChannel::Nightly => {
// Early nightly versions might not have stabilized the feature
// yet.
self.minor > minor
}
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum ReleaseChannel {
Stable,
Beta,
Nightly,
}
fn rustc_version() -> Option<Compiler> {
let rustc = env::var_os("RUSTC")?;
let output = Command::new(rustc).arg("--version").output().ok()?;
let version = str::from_utf8(&output.stdout).ok()?;
let mut pieces = version.split('.');
if pieces.next() != Some("rustc 1") {
return None;
}
let minor = pieces.next()?.parse().ok()?;
let channel = if version.contains("nightly") {
ReleaseChannel::Nightly
} else if version.contains("beta") {
ReleaseChannel::Beta
} else {
ReleaseChannel::Stable
};
Some(Compiler { minor, channel })
}
+213
View File
@@ -0,0 +1,213 @@
//! Color types for used for being generic over the color
use crate::{BgColorDisplay, BgDynColorDisplay, FgColorDisplay, FgDynColorDisplay};
use core::fmt;
macro_rules! colors {
($(
$color:ident $fg:literal $bg:literal
),* $(,)?) => {
pub(crate) mod ansi_colors {
use core::fmt;
#[allow(unused_imports)]
use crate::OwoColorize;
/// Available standard ANSI colors for use with [`OwoColorize::color`](OwoColorize::color)
/// or [`OwoColorize::on_color`](OwoColorize::on_color)
#[allow(missing_docs)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum AnsiColors {
$(
$color,
)*
}
impl crate::private::Sealed for AnsiColors {}
impl crate::DynColor for AnsiColors {
fn fmt_ansi_fg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let color = match self {
$(
AnsiColors::$color => concat!("\x1b[", stringify!($fg), "m"),
)*
};
write!(f, "{}", color)
}
fn fmt_ansi_bg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let color = match self {
$(
AnsiColors::$color => concat!("\x1b[", stringify!($bg), "m"),
)*
};
write!(f, "{}", color)
}
fn fmt_raw_ansi_fg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let color = match self {
$(
AnsiColors::$color => stringify!($fg),
)*
};
f.write_str(color)
}
fn fmt_raw_ansi_bg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let color = match self {
$(
AnsiColors::$color => stringify!($bg),
)*
};
f.write_str(color)
}
#[doc(hidden)]
fn get_dyncolors_fg(&self) -> crate::DynColors {
crate::DynColors::Ansi(*self)
}
#[doc(hidden)]
fn get_dyncolors_bg(&self) -> crate::DynColors {
crate::DynColors::Ansi(*self)
}
}
}
$(
/// A color for use with [`OwoColorize`](crate::OwoColorize)'s `fg` and `bg` methods.
pub struct $color;
impl crate::private::Sealed for $color {}
impl crate::Color for $color {
const ANSI_FG: &'static str = concat!("\x1b[", stringify!($fg), "m");
const ANSI_BG: &'static str = concat!("\x1b[", stringify!($bg), "m");
const RAW_ANSI_FG: &'static str = stringify!($fg);
const RAW_ANSI_BG: &'static str = stringify!($bg);
#[doc(hidden)]
type DynEquivalent = ansi_colors::AnsiColors;
#[doc(hidden)]
const DYN_EQUIVALENT: Self::DynEquivalent = ansi_colors::AnsiColors::$color;
#[doc(hidden)]
const DYN_COLORS_EQUIVALENT: crate::DynColors = crate::DynColors::Ansi(ansi_colors::AnsiColors::$color);
}
)*
};
}
colors! {
Black 30 40,
Red 31 41,
Green 32 42,
Yellow 33 43,
Blue 34 44,
Magenta 35 45,
Cyan 36 46,
White 37 47,
Default 39 49,
BrightBlack 90 100,
BrightRed 91 101,
BrightGreen 92 102,
BrightYellow 93 103,
BrightBlue 94 104,
BrightMagenta 95 105,
BrightCyan 96 106,
BrightWhite 97 107,
}
macro_rules! impl_fmt_for {
($($trait:path),* $(,)?) => {
$(
impl<'a, Color: crate::Color, T: ?Sized + $trait> $trait for FgColorDisplay<'a, Color, T> {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(Color::ANSI_FG)?;
<T as $trait>::fmt(&self.0, f)?;
f.write_str("\x1b[39m")
}
}
impl<'a, Color: crate::Color, T: ?Sized + $trait> $trait for BgColorDisplay<'a, Color, T> {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(Color::ANSI_BG)?;
<T as $trait>::fmt(&self.0, f)?;
f.write_str("\x1b[49m")
}
}
)*
};
}
impl_fmt_for! {
fmt::Display,
fmt::Debug,
fmt::UpperHex,
fmt::LowerHex,
fmt::Binary,
fmt::UpperExp,
fmt::LowerExp,
fmt::Octal,
fmt::Pointer,
}
macro_rules! impl_fmt_for_dyn {
($($trait:path),* $(,)?) => {
$(
impl<'a, Color: crate::DynColor, T: ?Sized + $trait> $trait for FgDynColorDisplay<'a, Color, T> {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(self.1).fmt_ansi_fg(f)?;
<T as $trait>::fmt(&self.0, f)?;
f.write_str("\x1b[39m")
}
}
impl<'a, Color: crate::DynColor, T: ?Sized + $trait> $trait for BgDynColorDisplay<'a, Color, T> {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(self.1).fmt_ansi_bg(f)?;
<T as $trait>::fmt(&self.0, f)?;
f.write_str("\x1b[49m")
}
}
)*
};
}
impl_fmt_for_dyn! {
fmt::Display,
fmt::Debug,
fmt::UpperHex,
fmt::LowerHex,
fmt::Binary,
fmt::UpperExp,
fmt::LowerExp,
fmt::Octal,
fmt::Pointer,
}
/// CSS named colors. Not as widely supported as standard ANSI as it relies on 48bit color support.
///
/// Reference: <https://www.w3schools.com/cssref/css_colors.asp>
/// Reference: <https://developer.mozilla.org/en-US/docs/Web/CSS/color_value>
pub mod css;
/// XTerm 256-bit colors. Not as widely supported as standard ANSI but contains 240 more colors.
pub mod xterm;
mod custom;
pub use custom::CustomColor;
pub(crate) mod dynamic;
+232
View File
@@ -0,0 +1,232 @@
macro_rules! css_color_types {
($(
$name:ident ($r:literal, $g:literal, $b:literal)
)*) => {
use crate::{Color, colors::CustomColor};
use core::fmt;
pub(crate) mod dynamic {
#[cfg(doc)]
use crate::OwoColorize;
/// Available CSS colors for use with [`OwoColorize::color`](OwoColorize::color)
/// or [`OwoColorize::on_color`](OwoColorize::on_color)
#[allow(missing_docs)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum CssColors {
$($name,)*
}
}
use dynamic::CssColors;
impl crate::private::Sealed for CssColors {}
impl crate::DynColor for CssColors {
fn fmt_ansi_fg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let color = match self {
$(
CssColors::$name => CustomColor::<$r, $g, $b>::ANSI_FG,
)*
};
f.write_str(color)
}
fn fmt_ansi_bg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let color = match self {
$(
CssColors::$name => CustomColor::<$r, $g, $b>::ANSI_BG,
)*
};
f.write_str(color)
}
fn fmt_raw_ansi_fg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let color = match self {
$(
CssColors::$name => CustomColor::<$r, $g, $b>::RAW_ANSI_FG,
)*
};
f.write_str(color)
}
fn fmt_raw_ansi_bg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let color = match self {
$(
CssColors::$name => CustomColor::<$r, $g, $b>::RAW_ANSI_BG,
)*
};
f.write_str(color)
}
#[doc(hidden)]
fn get_dyncolors_fg(&self) -> crate::DynColors {
crate::DynColors::Css(*self)
}
#[doc(hidden)]
fn get_dyncolors_bg(&self) -> crate::DynColors {
crate::DynColors::Css(*self)
}
}
$(
#[allow(missing_docs)]
pub type $name = CustomColor<$r, $g, $b>;
)*
};
}
css_color_types! {
AliceBlue (240, 248, 255)
AntiqueWhite (250, 235, 215)
Aqua (0, 255, 255)
Aquamarine (127, 255, 212)
Azure (240, 255, 255)
Beige (245, 245, 220)
Bisque (255, 228, 196)
Black (0, 0, 0)
BlanchedAlmond (255, 235, 205)
Blue (0, 0, 255)
BlueViolet (138, 43, 226)
Brown (165, 42, 42)
BurlyWood (222, 184, 135)
CadetBlue (95, 158, 160)
Chartreuse (127, 255, 0)
Chocolate (210, 105, 30)
Coral (255, 127, 80)
CornflowerBlue (100, 149, 237)
Cornsilk (255, 248, 220)
Crimson (220, 20, 60)
DarkBlue (0, 0, 139)
DarkCyan (0, 139, 139)
DarkGoldenRod (184, 134, 11)
DarkGray (169, 169, 169)
DarkGrey (169, 169, 169)
DarkGreen (0, 100, 0)
DarkKhaki (189, 183, 107)
DarkMagenta (139, 0, 139)
DarkOliveGreen (85, 107, 47)
DarkOrange (255, 140, 0)
DarkOrchid (153, 50, 204)
DarkRed (139, 0, 0)
DarkSalmon (233, 150, 122)
DarkSeaGreen (143, 188, 143)
DarkSlateBlue (72, 61, 139)
DarkSlateGray (47, 79, 79)
DarkSlateGrey (47, 79, 79)
DarkTurquoise (0, 206, 209)
DarkViolet (148, 0, 211)
DeepPink (255, 20, 147)
DeepSkyBlue (0, 191, 255)
DimGray (105, 105, 105)
DimGrey (105, 105, 105)
DodgerBlue (30, 144, 255)
FireBrick (178, 34, 34)
FloralWhite (255, 250, 240)
ForestGreen (34, 139, 34)
Fuchsia (255, 0, 255)
Gainsboro (220, 220, 220)
GhostWhite (248, 248, 255)
Gold (255, 215, 0)
GoldenRod (218, 165, 32)
Gray (128, 128, 128)
Grey (128, 128, 128)
Green (0, 128, 0)
GreenYellow (173, 255, 47)
HoneyDew (240, 255, 240)
HotPink (255, 105, 180)
IndianRed (205, 92, 92)
Indigo (75, 0, 130)
Ivory (255, 255, 240)
Khaki (240, 230, 140)
Lavender (230, 230, 250)
LavenderBlush (255, 240, 245)
LawnGreen (124, 252, 0)
LemonChiffon (255, 250, 205)
LightBlue (173, 216, 230)
LightCoral (240, 128, 128)
LightCyan (224, 255, 255)
LightGoldenRodYellow (250, 250, 210)
LightGray (211, 211, 211)
LightGrey (211, 211, 211)
LightGreen (144, 238, 144)
LightPink (255, 182, 193)
LightSalmon (255, 160, 122)
LightSeaGreen (32, 178, 170)
LightSkyBlue (135, 206, 250)
LightSlateGray (119, 136, 153)
LightSlateGrey (119, 136, 153)
LightSteelBlue (176, 196, 222)
LightYellow (255, 255, 224)
Lime (0, 255, 0)
LimeGreen (50, 205, 50)
Linen (250, 240, 230)
Magenta (255, 0, 255)
Maroon (128, 0, 0)
MediumAquaMarine (102, 205, 170)
MediumBlue (0, 0, 205)
MediumOrchid (186, 85, 211)
MediumPurple (147, 112, 219)
MediumSeaGreen (60, 179, 113)
MediumSlateBlue (123, 104, 238)
MediumSpringGreen (0, 250, 154)
MediumTurquoise (72, 209, 204)
MediumVioletRed (199, 21, 133)
MidnightBlue (25, 25, 112)
MintCream (245, 255, 250)
MistyRose (255, 228, 225)
Moccasin (255, 228, 181)
NavajoWhite (255, 222, 173)
Navy (0, 0, 128)
OldLace (253, 245, 230)
Olive (128, 128, 0)
OliveDrab (107, 142, 35)
Orange (255, 165, 0)
OrangeRed (255, 69, 0)
Orchid (218, 112, 214)
PaleGoldenRod (238, 232, 170)
PaleGreen (152, 251, 152)
PaleTurquoise (175, 238, 238)
PaleVioletRed (219, 112, 147)
PapayaWhip (255, 239, 213)
PeachPuff (255, 218, 185)
Peru (205, 133, 63)
Pink (255, 192, 203)
Plum (221, 160, 221)
PowderBlue (176, 224, 230)
Purple (128, 0, 128)
RebeccaPurple (102, 51, 153)
Red (255, 0, 0)
RosyBrown (188, 143, 143)
RoyalBlue (65, 105, 225)
SaddleBrown (139, 69, 19)
Salmon (250, 128, 114)
SandyBrown (244, 164, 96)
SeaGreen (46, 139, 87)
SeaShell (255, 245, 238)
Sienna (160, 82, 45)
Silver (192, 192, 192)
SkyBlue (135, 206, 235)
SlateBlue (106, 90, 205)
SlateGray (112, 128, 144)
SlateGrey (112, 128, 144)
Snow (255, 250, 250)
SpringGreen (0, 255, 127)
SteelBlue (70, 130, 180)
Tan (210, 180, 140)
Teal (0, 128, 128)
Thistle (216, 191, 216)
Tomato (255, 99, 71)
Turquoise (64, 224, 208)
Violet (238, 130, 238)
Wheat (245, 222, 179)
White (255, 255, 255)
WhiteSmoke (245, 245, 245)
Yellow (255, 255, 0)
YellowGreen (154, 205, 50)
}
+116
View File
@@ -0,0 +1,116 @@
use crate::Color;
const U8_TO_STR: [[u8; 3]; 256] = generate_lookup();
const fn generate_lookup() -> [[u8; 3]; 256] {
let mut table = [[0, 0, 0]; 256];
let mut i = 0;
while i < 256 {
table[i] = [
b'0' + (i / 100) as u8,
b'0' + (i / 10 % 10) as u8,
b'0' + (i % 10) as u8,
];
i += 1;
}
table
}
enum Plane {
Fg,
Bg,
}
const fn rgb_to_ansi(r: u8, g: u8, b: u8, plane: Plane) -> [u8; 19] {
let mut buf = match plane {
Plane::Fg => *b"\x1b[38;2;rrr;ggg;bbbm",
Plane::Bg => *b"\x1b[48;2;rrr;ggg;bbbm",
};
let r = U8_TO_STR[r as usize];
let g = U8_TO_STR[g as usize];
let b = U8_TO_STR[b as usize];
// r 7
buf[7] = r[0];
buf[8] = r[1];
buf[9] = r[2];
// g 11
buf[11] = g[0];
buf[12] = g[1];
buf[13] = g[2];
// b 15
buf[15] = b[0];
buf[16] = b[1];
buf[17] = b[2];
buf
}
const fn rgb_to_ansi_color(r: u8, g: u8, b: u8, plane: Plane) -> [u8; 16] {
let mut buf = match plane {
Plane::Fg => *b"38;2;rrr;ggg;bbb",
Plane::Bg => *b"48;2;rrr;ggg;bbb",
};
let r = U8_TO_STR[r as usize];
let g = U8_TO_STR[g as usize];
let b = U8_TO_STR[b as usize];
// r 5
buf[5] = r[0];
buf[6] = r[1];
buf[7] = r[2];
// g 9
buf[9] = g[0];
buf[10] = g[1];
buf[11] = g[2];
// b 13
buf[13] = b[0];
buf[14] = b[1];
buf[15] = b[2];
buf
}
/// A custom RGB color, determined at compile time
pub struct CustomColor<const R: u8, const G: u8, const B: u8>;
/// This exists since unwrap() isn't const-safe (it invokes formatting infrastructure)
const fn bytes_to_str(bytes: &'static [u8]) -> &'static str {
match core::str::from_utf8(bytes) {
Ok(o) => o,
Err(_e) => panic!("Const parsing &[u8] to a string failed!"),
}
}
impl<const R: u8, const G: u8, const B: u8> CustomColor<R, G, B> {
const ANSI_FG_U8: [u8; 19] = rgb_to_ansi(R, G, B, Plane::Fg);
const ANSI_BG_U8: [u8; 19] = rgb_to_ansi(R, G, B, Plane::Bg);
const RAW_ANSI_FG_U8: [u8; 16] = rgb_to_ansi_color(R, G, B, Plane::Fg);
const RAW_ANSI_BG_U8: [u8; 16] = rgb_to_ansi_color(R, G, B, Plane::Bg);
}
impl<const R: u8, const G: u8, const B: u8> crate::private::Sealed for CustomColor<R, G, B> {}
impl<const R: u8, const G: u8, const B: u8> Color for CustomColor<R, G, B> {
const ANSI_FG: &'static str = bytes_to_str(&Self::ANSI_FG_U8);
const ANSI_BG: &'static str = bytes_to_str(&Self::ANSI_BG_U8);
const RAW_ANSI_FG: &'static str = bytes_to_str(&Self::RAW_ANSI_FG_U8);
const RAW_ANSI_BG: &'static str = bytes_to_str(&Self::RAW_ANSI_BG_U8);
#[doc(hidden)]
type DynEquivalent = crate::Rgb;
#[doc(hidden)]
const DYN_EQUIVALENT: Self::DynEquivalent = crate::Rgb(R, G, B);
#[doc(hidden)]
const DYN_COLORS_EQUIVALENT: crate::DynColors = crate::DynColors::Rgb(R, G, B);
}
+105
View File
@@ -0,0 +1,105 @@
use crate::{AnsiColors, DynColor};
use core::fmt;
#[allow(unused_imports)]
use crate::OwoColorize;
/// Available RGB colors for use with [`OwoColorize::color`](OwoColorize::color)
/// or [`OwoColorize::on_color`](OwoColorize::on_color)
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Rgb(pub u8, pub u8, pub u8);
impl crate::private::Sealed for Rgb {}
impl DynColor for Rgb {
fn fmt_ansi_fg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Rgb(r, g, b) = self;
write!(f, "\x1b[38;2;{r};{g};{b}m")
}
fn fmt_ansi_bg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Rgb(r, g, b) = self;
write!(f, "\x1b[48;2;{r};{g};{b}m")
}
fn fmt_raw_ansi_fg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Rgb(r, g, b) = self;
write!(f, "38;2;{r};{g};{b}")
}
fn fmt_raw_ansi_bg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Rgb(r, g, b) = self;
write!(f, "48;2;{r};{g};{b}")
}
#[doc(hidden)]
fn get_dyncolors_fg(&self) -> crate::DynColors {
let Rgb(r, g, b) = self;
crate::DynColors::Rgb(*r, *g, *b)
}
#[doc(hidden)]
fn get_dyncolors_bg(&self) -> crate::DynColors {
self.get_dyncolors_fg()
}
}
impl crate::private::Sealed for str {}
impl DynColor for str {
fn fmt_ansi_fg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let color: AnsiColors = self.into();
color.fmt_ansi_fg(f)
}
fn fmt_ansi_bg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let color: AnsiColors = self.into();
color.fmt_ansi_bg(f)
}
fn fmt_raw_ansi_fg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let color: AnsiColors = self.into();
color.fmt_raw_ansi_fg(f)
}
fn fmt_raw_ansi_bg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let color: AnsiColors = self.into();
color.fmt_raw_ansi_bg(f)
}
#[doc(hidden)]
fn get_dyncolors_fg(&self) -> crate::DynColors {
crate::DynColors::Ansi(self.into())
}
#[doc(hidden)]
fn get_dyncolors_bg(&self) -> crate::DynColors {
crate::DynColors::Ansi(self.into())
}
}
/// Implemented for drop-in replacement support for `colored`
impl<'a> From<&'a str> for AnsiColors {
fn from(color: &'a str) -> Self {
#[allow(clippy::match_same_arms)] // defaults to white color
match color {
"black" => AnsiColors::Black,
"red" => AnsiColors::Red,
"green" => AnsiColors::Green,
"yellow" => AnsiColors::Yellow,
"blue" => AnsiColors::Blue,
"magenta" | "purple" => AnsiColors::Magenta,
"cyan" => AnsiColors::Cyan,
"white" => AnsiColors::White,
"bright black" => AnsiColors::BrightBlack,
"bright red" => AnsiColors::BrightRed,
"bright green" => AnsiColors::BrightGreen,
"bright yellow" => AnsiColors::BrightYellow,
"bright blue" => AnsiColors::BrightBlue,
"bright magenta" => AnsiColors::BrightMagenta,
"bright cyan" => AnsiColors::BrightCyan,
"bright white" => AnsiColors::BrightWhite,
_ => AnsiColors::White,
}
}
}
+380
View File
@@ -0,0 +1,380 @@
macro_rules! xterm_colors {
($(
$xterm_num:literal $name:ident ($r:literal, $g:literal, $b:literal)
)*) => {
pub(crate) mod dynamic {
use core::fmt;
#[allow(unused_imports)]
use crate::OwoColorize;
/// Available Xterm colors for use with [`OwoColorize::color`](OwoColorize::color)
/// or [`OwoColorize::on_color`](OwoColorize::on_color)
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum XtermColors {
$(
#[allow(missing_docs)]
$name,
)*
}
impl crate::private::Sealed for XtermColors {}
impl crate::DynColor for XtermColors {
fn fmt_ansi_fg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let color = match self {
$(
XtermColors::$name => concat!("\x1b[38;5;", stringify!($xterm_num), "m"),
)*
};
f.write_str(color)
}
fn fmt_ansi_bg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let color = match self {
$(
XtermColors::$name => concat!("\x1b[48;5;", stringify!($xterm_num), "m"),
)*
};
f.write_str(color)
}
fn fmt_raw_ansi_fg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let color = match self {
$(
XtermColors::$name => concat!("38;5;", stringify!($xterm_num)),
)*
};
f.write_str(color)
}
fn fmt_raw_ansi_bg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let color = match self {
$(
XtermColors::$name => concat!("48;5;", stringify!($xterm_num)),
)*
};
f.write_str(color)
}
#[doc(hidden)]
fn get_dyncolors_fg(&self) -> crate::DynColors {
crate::DynColors::Xterm(*self)
}
#[doc(hidden)]
fn get_dyncolors_bg(&self) -> crate::DynColors {
crate::DynColors::Xterm(*self)
}
}
impl From<u8> for XtermColors {
fn from(x: u8) -> Self {
match x {
$(
$xterm_num => XtermColors::$name,
)*
}
}
}
impl From<XtermColors> for u8 {
fn from(color: XtermColors) -> Self {
match color {
$(
XtermColors::$name => $xterm_num,
)*
}
}
}
}
$(
#[allow(missing_docs)]
pub struct $name;
impl crate::private::Sealed for $name {}
impl crate::Color for $name {
const ANSI_FG: &'static str = concat!("\x1b[38;5;", stringify!($xterm_num), "m");
const ANSI_BG: &'static str = concat!("\x1b[48;5;", stringify!($xterm_num), "m");
const RAW_ANSI_BG: &'static str = concat!("48;5;", stringify!($xterm_num));
const RAW_ANSI_FG: &'static str = concat!("38;5;", stringify!($xterm_num));
#[doc(hidden)]
type DynEquivalent = dynamic::XtermColors;
#[doc(hidden)]
const DYN_EQUIVALENT: Self::DynEquivalent = dynamic::XtermColors::$name;
#[doc(hidden)]
const DYN_COLORS_EQUIVALENT: crate::DynColors = crate::DynColors::Xterm(Self::DYN_EQUIVALENT);
}
)*
};
}
xterm_colors! {
0 UserBlack (0,0,0)
1 UserRed (128,0,0)
2 UserGreen (0,128,0)
3 UserYellow (128,128,0)
4 UserBlue (0,0,128)
5 UserMagenta (128,0,128)
6 UserCyan (0,128,128)
7 UserWhite (192,192,192)
8 UserBrightBlack (128,128,128)
9 UserBrightRed (255,0,0)
10 UserBrightGreen (0,255,0)
11 UserBrightYellow (255,255,0)
12 UserBrightBlue (0,0,255)
13 UserBrightMagenta (255,0,255)
14 UserBrightCyan (0,255,255)
15 UserBrightWhite (255,255,255)
16 Black (0,0,0)
17 StratosBlue (0,0,95)
18 NavyBlue (0,0,135)
19 MidnightBlue (0,0,175)
20 DarkBlue (0,0,215)
21 Blue (0,0,255)
22 CamaroneGreen (0,95,0)
23 BlueStone (0,95,95)
24 OrientBlue (0,95,135)
25 EndeavourBlue (0,95,175)
26 ScienceBlue (0,95,215)
27 BlueRibbon (0,95,255)
28 JapaneseLaurel (0,135,0)
29 DeepSeaGreen (0,135,95)
30 Teal (0,135,135)
31 DeepCerulean (0,135,175)
32 LochmaraBlue (0,135,215)
33 AzureRadiance (0,135,255)
34 LightJapaneseLaurel (0,175,0)
35 Jade (0,175,95)
36 PersianGreen (0,175,135)
37 BondiBlue (0,175,175)
38 Cerulean (0,175,215)
39 LightAzureRadiance (0,175,255)
40 DarkGreen (0,215,0)
41 Malachite (0,215,95)
42 CaribbeanGreen (0,215,135)
43 LightCaribbeanGreen (0,215,175)
44 RobinEggBlue (0,215,215)
45 Aqua (0,215,255)
46 Green (0,255,0)
47 DarkSpringGreen (0,255,95)
48 SpringGreen (0,255,135)
49 LightSpringGreen (0,255,175)
50 BrightTurquoise (0,255,215)
51 Cyan (0,255,255)
52 Rosewood (95,0,0)
53 PompadourMagenta (95,0,95)
54 PigmentIndigo (95,0,135)
55 DarkPurple (95,0,175)
56 ElectricIndigo (95,0,215)
57 ElectricPurple (95,0,255)
58 VerdunGreen (95,95,0)
59 ScorpionOlive (95,95,95)
60 Lilac (95,95,135)
61 ScampiIndigo (95,95,175)
62 Indigo (95,95,215)
63 DarkCornflowerBlue (95,95,255)
64 DarkLimeade (95,135,0)
65 GladeGreen (95,135,95)
66 JuniperGreen (95,135,135)
67 HippieBlue (95,135,175)
68 HavelockBlue (95,135,215)
69 CornflowerBlue (95,135,255)
70 Limeade (95,175,0)
71 FernGreen (95,175,95)
72 SilverTree (95,175,135)
73 Tradewind (95,175,175)
74 ShakespeareBlue (95,175,215)
75 DarkMalibuBlue (95,175,255)
76 DarkBrightGreen (95,215,0)
77 DarkPastelGreen (95,215,95)
78 PastelGreen (95,215,135)
79 DownyTeal (95,215,175)
80 Viking (95,215,215)
81 MalibuBlue (95,215,255)
82 BrightGreen (95,255,0)
83 DarkScreaminGreen (95,255,95)
84 ScreaminGreen (95,255,135)
85 DarkAquamarine (95,255,175)
86 Aquamarine (95,255,215)
87 LightAquamarine (95,255,255)
88 Maroon (135,0,0)
89 DarkFreshEggplant (135,0,95)
90 LightFreshEggplant (135,0,135)
91 Purple (135,0,175)
92 ElectricViolet (135,0,215)
93 LightElectricViolet (135,0,255)
94 Brown (135,95,0)
95 CopperRose (135,95,95)
96 StrikemasterPurple (135,95,135)
97 DelugePurple (135,95,175)
98 DarkMediumPurple (135,95,215)
99 DarkHeliotropePurple (135,95,255)
100 Olive (135,135,0)
101 ClayCreekOlive (135,135,95)
102 DarkGray (135,135,135)
103 WildBlueYonder (135,135,175)
104 ChetwodeBlue (135,135,215)
105 SlateBlue (135,135,255)
106 LightLimeade (135,175,0)
107 ChelseaCucumber (135,175,95)
108 BayLeaf (135,175,135)
109 GulfStream (135,175,175)
110 PoloBlue (135,175,215)
111 LightMalibuBlue (135,175,255)
112 Pistachio (135,215,0)
113 LightPastelGreen (135,215,95)
114 DarkFeijoaGreen (135,215,135)
115 VistaBlue (135,215,175)
116 Bermuda (135,215,215)
117 DarkAnakiwaBlue (135,215,255)
118 ChartreuseGreen (135,255,0)
119 LightScreaminGreen (135,255,95)
120 DarkMintGreen (135,255,135)
121 MintGreen (135,255,175)
122 LighterAquamarine (135,255,215)
123 AnakiwaBlue (135,255,255)
124 BrightRed (175,0,0)
125 DarkFlirt (175,0,95)
126 Flirt (175,0,135)
127 LightFlirt (175,0,175)
128 DarkViolet (175,0,215)
129 BrightElectricViolet (175,0,255)
130 RoseofSharonOrange (175,95,0)
131 MatrixPink (175,95,95)
132 TapestryPink (175,95,135)
133 FuchsiaPink (175,95,175)
134 MediumPurple (175,95,215)
135 Heliotrope (175,95,255)
136 PirateGold (175,135,0)
137 MuesliOrange (175,135,95)
138 PharlapPink (175,135,135)
139 Bouquet (175,135,175)
140 Lavender (175,135,215)
141 LightHeliotrope (175,135,255)
142 BuddhaGold (175,175,0)
143 OliveGreen (175,175,95)
144 HillaryOlive (175,175,135)
145 SilverChalice (175,175,175)
146 WistfulLilac (175,175,215)
147 MelroseLilac (175,175,255)
148 RioGrandeGreen (175,215,0)
149 ConiferGreen (175,215,95)
150 Feijoa (175,215,135)
151 PixieGreen (175,215,175)
152 JungleMist (175,215,215)
153 LightAnakiwaBlue (175,215,255)
154 Lime (175,255,0)
155 GreenYellow (175,255,95)
156 LightMintGreen (175,255,135)
157 Celadon (175,255,175)
158 AeroBlue (175,255,215)
159 FrenchPassLightBlue (175,255,255)
160 GuardsmanRed (215,0,0)
161 RazzmatazzCerise (215,0,95)
162 MediumVioletRed (215,0,135)
163 HollywoodCerise (215,0,175)
164 DarkPurplePizzazz (215,0,215)
165 BrighterElectricViolet (215,0,255)
166 TennOrange (215,95,0)
167 RomanOrange (215,95,95)
168 CranberryPink (215,95,135)
169 HopbushPink (215,95,175)
170 Orchid (215,95,215)
171 LighterHeliotrope (215,95,255)
172 MangoTango (215,135,0)
173 Copperfield (215,135,95)
174 SeaPink (215,135,135)
175 CanCanPink (215,135,175)
176 LightOrchid (215,135,215)
177 BrightHeliotrope (215,135,255)
178 DarkCorn (215,175,0)
179 DarkTachaOrange (215,175,95)
180 TanBeige (215,175,135)
181 ClamShell (215,175,175)
182 ThistlePink (215,175,215)
183 Mauve (215,175,255)
184 Corn (215,215,0)
185 TachaOrange (215,215,95)
186 DecoOrange (215,215,135)
187 PaleGoldenrod (215,215,175)
188 AltoBeige (215,215,215)
189 FogPink (215,215,255)
190 ChartreuseYellow (215,255,0)
191 Canary (215,255,95)
192 Honeysuckle (215,255,135)
193 ReefPaleYellow (215,255,175)
194 SnowyMint (215,255,215)
195 OysterBay (215,255,255)
196 Red (255,0,0)
197 DarkRose (255,0,95)
198 Rose (255,0,135)
199 LightHollywoodCerise (255,0,175)
200 PurplePizzazz (255,0,215)
201 Fuchsia (255,0,255)
202 BlazeOrange (255,95,0)
203 BittersweetOrange (255,95,95)
204 WildWatermelon (255,95,135)
205 DarkHotPink (255,95,175)
206 HotPink (255,95,215)
207 PinkFlamingo (255,95,255)
208 FlushOrange (255,135,0)
209 Salmon (255,135,95)
210 VividTangerine (255,135,135)
211 PinkSalmon (255,135,175)
212 DarkLavenderRose (255,135,215)
213 BlushPink (255,135,255)
214 YellowSea (255,175,0)
215 TexasRose (255,175,95)
216 Tacao (255,175,135)
217 Sundown (255,175,175)
218 CottonCandy (255,175,215)
219 LavenderRose (255,175,255)
220 Gold (255,215,0)
221 Dandelion (255,215,95)
222 GrandisCaramel (255,215,135)
223 Caramel (255,215,175)
224 CosmosSalmon (255,215,215)
225 PinkLace (255,215,255)
226 Yellow (255,255,0)
227 LaserLemon (255,255,95)
228 DollyYellow (255,255,135)
229 PortafinoYellow (255,255,175)
230 Cumulus (255,255,215)
231 White (255,255,255)
232 DarkCodGray (8,8,8)
233 CodGray (18,18,18)
234 LightCodGray (28,28,28)
235 DarkMineShaft (38,38,38)
236 MineShaft (48,48,48)
237 LightMineShaft (58,58,58)
238 DarkTundora (68,68,68)
239 Tundora (78,78,78)
240 ScorpionGray (88,88,88)
241 DarkDoveGray (98,98,98)
242 DoveGray (108,108,108)
243 Boulder (118,118,118)
244 Gray (128,128,128)
245 LightGray (138,138,138)
246 DustyGray (148,148,148)
247 NobelGray (158,158,158)
248 DarkSilverChalice (168,168,168)
249 LightSilverChalice (178,178,178)
250 DarkSilver (188,188,188)
251 Silver (198,198,198)
252 DarkAlto (208,208,208)
253 Alto (218,218,218)
254 Mercury (228,228,228)
255 GalleryGray (238,238,238)
}
+945
View File
@@ -0,0 +1,945 @@
use crate::{BgColorDisplay, Color, FgColorDisplay};
use crate::{BgDynColorDisplay, DynColor, FgDynColorDisplay, Style, Styled, colors};
use core::fmt;
use core::marker::PhantomData;
#[cfg(doc)]
use crate::OwoColorize;
/// A wrapper type which applies both a foreground and background color
pub struct ComboColorDisplay<'a, Fg: Color, Bg: Color, T: ?Sized>(&'a T, PhantomData<(Fg, Bg)>);
/// Wrapper around a type which implements all the formatters the wrapped type does, with the
/// addition of changing the foreground and background color.
///
/// If compile-time coloring is an option, consider using [`ComboColorDisplay`] instead.
pub struct ComboDynColorDisplay<'a, Fg: DynColor, Bg: DynColor, T: ?Sized>(&'a T, Fg, Bg);
macro_rules! impl_fmt_for_combo {
($($trait:path),* $(,)?) => {
$(
impl<'a, Fg: Color, Bg: Color, T: ?Sized + $trait> $trait for ComboColorDisplay<'a, Fg, Bg, T> {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("\x1b[")?;
f.write_str(Fg::RAW_ANSI_FG)?;
f.write_str(";")?;
f.write_str(Bg::RAW_ANSI_BG)?;
f.write_str("m")?;
<T as $trait>::fmt(&self.0, f)?;
f.write_str("\x1b[0m")
}
}
)*
$(
impl<'a, Fg: DynColor, Bg: DynColor, T: ?Sized + $trait> $trait for ComboDynColorDisplay<'a, Fg, Bg, T> {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("\x1b[")?;
self.1.fmt_raw_ansi_fg(f)?;
f.write_str(";")?;
self.2.fmt_raw_ansi_bg(f)?;
f.write_str("m")?;
<T as $trait>::fmt(&self.0, f)?;
f.write_str("\x1b[0m")
}
}
)*
};
}
impl_fmt_for_combo! {
fmt::Display,
fmt::Debug,
fmt::UpperHex,
fmt::LowerHex,
fmt::Binary,
fmt::UpperExp,
fmt::LowerExp,
fmt::Octal,
fmt::Pointer,
}
/// implement specialized color methods for FgColorDisplay BgColorDisplay, ComboColorDisplay
macro_rules! color_methods {
($(
#[$fg_meta:meta] #[$bg_meta:meta] $color:ident $fg_method:ident $bg_method:ident
),* $(,)?) => {
const _: () = (); // workaround for syntax highlighting bug
impl<'a, Fg, T: ?Sized> FgColorDisplay<'a, Fg, T>
where
Fg: Color,
{
/// Create a new [`FgColorDisplay`], from a reference to a type which implements
/// [`Color`].
///
/// This is a const function: in non-const contexts, [`OwoColorize::fg`] or one of the
/// other methods on it may be more convenient.
///
/// # Example
///
/// Usage in const contexts:
///
/// ```rust
/// use owo_colors::{colors::Green, FgColorDisplay};
///
/// const GREEN_TEXT: FgColorDisplay<Green, str> = FgColorDisplay::new("green");
///
/// println!("{}", GREEN_TEXT);
/// # assert_eq!(format!("{}", GREEN_TEXT), "\x1b[32mgreen\x1b[39m");
/// ```
pub const fn new(thing: &'a T) -> Self {
Self(thing, PhantomData)
}
/// Convert self to a generic [`Styled`].
///
/// This method erases color-related type parameters, and can be
/// used to unify types across branches.
///
/// # Example
///
/// Typical use:
///
/// ```rust
/// use owo_colors::OwoColorize;
///
/// fn is_blue() -> bool {
/// // ...
/// # true
/// }
///
/// let styled_str = if is_blue() {
/// "hello".blue().into_styled()
/// } else {
/// "hello".green().into_styled()
/// };
///
/// println!("{}", styled_str);
/// # assert_eq!(format!("{}", styled_str), "\x1b[34mhello\x1b[0m");
/// ```
///
/// Usage in const contexts:
///
/// ```rust
/// use owo_colors::{colors::{Blue, Green}, FgColorDisplay, Styled};
///
/// const fn is_blue() -> bool {
/// // ...
/// # true
/// }
///
/// const STYLED_STR: Styled<&str> = if is_blue() {
/// FgColorDisplay::<Blue, _>::new("Hello").into_styled()
/// } else {
/// FgColorDisplay::<Green, _>::new("Hello").into_styled()
/// };
///
/// println!("{}", STYLED_STR);
/// # assert_eq!(format!("{}", STYLED_STR), "\x1b[34mHello\x1b[0m");
/// ```
pub const fn into_styled(self) -> Styled<&'a T> {
let style = Style::new().fg::<Fg>();
Styled { style, target: self.0 }
}
/// Set the foreground color at runtime. Only use if you do not know which color will be used at
/// compile-time. If the color is constant, use either [`OwoColorize::fg`] or
/// a color-specific method, such as [`OwoColorize::green`],
///
/// ```rust
/// use owo_colors::{OwoColorize, AnsiColors};
///
/// println!("{}", "green".color(AnsiColors::Green));
/// ```
pub const fn color<NewFg: DynColor>(
self,
fg: NewFg,
) -> FgDynColorDisplay<'a, NewFg, T> {
FgDynColorDisplay(self.0, fg)
}
/// Set the background color at runtime. Only use if you do not know what color to use at
/// compile-time. If the color is constant, use either [`OwoColorize::bg`] or
/// a color-specific method, such as [`OwoColorize::on_yellow`],
///
/// ```rust
/// use owo_colors::{OwoColorize, AnsiColors};
///
/// println!("{}", "yellow background".on_color(AnsiColors::BrightYellow));
/// ```
pub const fn on_color<NewBg: DynColor>(
self,
bg: NewBg,
) -> ComboDynColorDisplay<'a, Fg::DynEquivalent, NewBg, T> {
ComboDynColorDisplay(self.0, Fg::DYN_EQUIVALENT, bg)
}
/// Set the foreground color generically
///
/// ```rust
/// use owo_colors::{OwoColorize, colors::*};
///
/// println!("{}", "red foreground".fg::<Red>());
/// ```
pub const fn fg<C: Color>(self) -> FgColorDisplay<'a, C, T> {
FgColorDisplay(self.0, PhantomData)
}
/// Set the background color generically.
///
/// ```rust
/// use owo_colors::{OwoColorize, colors::*};
///
/// println!("{}", "black background".bg::<Black>());
/// ```
pub const fn bg<C: Color>(self) -> ComboColorDisplay<'a, Fg, C, T> {
ComboColorDisplay(self.0, PhantomData)
}
$(
#[$fg_meta]
#[inline(always)]
pub const fn $fg_method(self) -> FgColorDisplay<'a, colors::$color, T> {
FgColorDisplay(self.0, PhantomData)
}
#[$bg_meta]
#[inline(always)]
pub const fn $bg_method(self) -> ComboColorDisplay<'a, Fg, colors::$color, T> {
ComboColorDisplay(self.0, PhantomData)
}
)*
}
const _: () = (); // workaround for syntax highlighting bug
impl<'a, Bg, T: ?Sized> BgColorDisplay<'a, Bg, T>
where
Bg: Color,
{
/// Create a new [`BgColorDisplay`], from a reference to a type which implements
/// [`Color`].
///
/// This is a const function: in non-const contexts, [`OwoColorize::bg`] may be more
/// convenient.
///
/// # Example
///
/// Usage in const contexts:
///
/// ```rust
/// use owo_colors::{colors::Red, BgColorDisplay};
///
/// const RED_BG_TEXT: BgColorDisplay<Red, str> = BgColorDisplay::new("red background");
///
/// println!("{}", RED_BG_TEXT);
/// # assert_eq!(format!("{}", RED_BG_TEXT), "\x1b[41mred background\x1b[49m");
/// ```
pub const fn new(thing: &'a T) -> Self {
Self(thing, PhantomData)
}
/// Convert self to a generic [`Styled`].
///
/// This method erases color-related type parameters, and can be
/// used to unify types across branches.
///
/// # Example
///
/// Typical use:
///
/// ```rust
/// use owo_colors::OwoColorize;
///
/// fn is_red() -> bool {
/// // ...
/// # true
/// }
///
/// let styled_str = if is_red() {
/// "hello".on_red().into_styled()
/// } else {
/// "hello".on_yellow().into_styled()
/// };
///
/// println!("{}", styled_str);
/// # assert_eq!(format!("{}", styled_str), "\x1b[41mhello\x1b[0m");
/// ```
///
/// Usage in const contexts:
///
/// ```rust
/// use owo_colors::{colors::{Red, Yellow}, BgColorDisplay, Styled};
///
/// const fn is_red() -> bool {
/// // ...
/// # true
/// }
///
/// const STYLED_STR: Styled<&str> = if is_red() {
/// BgColorDisplay::<Red, _>::new("Hello").into_styled()
/// } else {
/// BgColorDisplay::<Yellow, _>::new("Hello").into_styled()
/// };
///
/// println!("{}", STYLED_STR);
/// # assert_eq!(format!("{}", STYLED_STR), "\x1b[41mHello\x1b[0m");
/// ```
pub const fn into_styled(self) -> Styled<&'a T> {
let style = Style::new().bg::<Bg>();
Styled { style, target: self.0 }
}
/// Set the foreground color at runtime. Only use if you do not know which color will be used at
/// compile-time. If the color is constant, use either [`OwoColorize::fg`] or
/// a color-specific method, such as [`OwoColorize::green`],
///
/// ```rust
/// use owo_colors::{OwoColorize, AnsiColors};
///
/// println!("{}", "green".color(AnsiColors::Green));
/// ```
pub const fn color<NewFg: DynColor>(
self,
fg: NewFg,
) -> ComboDynColorDisplay<'a, NewFg, Bg::DynEquivalent, T> {
ComboDynColorDisplay(self.0, fg, Bg::DYN_EQUIVALENT)
}
/// Set the background color at runtime. Only use if you do not know what color to use at
/// compile-time. If the color is constant, use either [`OwoColorize::bg`] or
/// a color-specific method, such as [`OwoColorize::on_yellow`],
///
/// ```rust
/// use owo_colors::{OwoColorize, AnsiColors};
///
/// println!("{}", "yellow background".on_color(AnsiColors::BrightYellow));
/// ```
pub const fn on_color<NewBg: DynColor>(
self,
bg: NewBg,
) -> BgDynColorDisplay<'a, NewBg, T> {
BgDynColorDisplay(self.0, bg)
}
/// Set the foreground color generically
///
/// ```rust
/// use owo_colors::{OwoColorize, colors::*};
///
/// println!("{}", "red foreground".fg::<Red>());
/// ```
pub const fn fg<C: Color>(self) -> ComboColorDisplay<'a, C, Bg, T> {
ComboColorDisplay(self.0, PhantomData)
}
/// Set the background color generically.
///
/// ```rust
/// use owo_colors::{OwoColorize, colors::*};
///
/// println!("{}", "black background".bg::<Black>());
/// ```
pub const fn bg<C: Color>(self) -> BgColorDisplay<'a, C, T> {
BgColorDisplay(self.0, PhantomData)
}
$(
#[$bg_meta]
#[inline(always)]
pub const fn $bg_method(self) -> BgColorDisplay<'a, colors::$color, T> {
BgColorDisplay(self.0, PhantomData)
}
#[$fg_meta]
#[inline(always)]
pub const fn $fg_method(self) -> ComboColorDisplay<'a, colors::$color, Bg, T> {
ComboColorDisplay(self.0, PhantomData)
}
)*
}
const _: () = (); // workaround for syntax highlighting bug
impl<'a, Fg, Bg, T: ?Sized> ComboColorDisplay<'a, Fg, Bg, T>
where
Fg: Color,
Bg: Color,
{
/// Create a new [`ComboColorDisplay`], from a pair of foreground and background types
/// which implement [`Color`].
///
/// This is a const function: in non-const contexts, calling the [`OwoColorize`]
/// functions may be more convenient.
///
/// # Example
///
/// Usage in const contexts:
///
/// ```rust
/// use owo_colors::{colors::{Blue, White}, ComboColorDisplay};
///
/// const COMBO_TEXT: ComboColorDisplay<Blue, White, str> =
/// ComboColorDisplay::new("blue text on white background");
///
/// println!("{}", COMBO_TEXT);
/// # assert_eq!(format!("{}", COMBO_TEXT), "\x1b[34;47mblue text on white background\x1b[0m");
/// ```
pub const fn new(thing: &'a T) -> Self {
Self(thing, PhantomData)
}
/// Convert self to a generic [`Styled`].
///
/// This method erases color-related type parameters, and can be
/// used to unify types across branches.
///
/// # Example
///
/// Typical use:
///
/// ```rust
/// use owo_colors::OwoColorize;
///
/// fn is_black_on_white() -> bool {
/// // ...
/// # true
/// }
///
/// let styled_str = if is_black_on_white() {
/// "hello".black().on_white().into_styled()
/// } else {
/// "hello".white().on_black().into_styled()
/// };
///
/// println!("{}", styled_str);
/// # assert_eq!(format!("{}", styled_str), "\x1b[30;47mhello\x1b[0m");
/// ```
///
/// Usage in const contexts:
///
/// ```rust
/// use owo_colors::{colors::{Black, White}, ComboColorDisplay, Styled};
///
/// const fn is_black_on_white() -> bool {
/// // ...
/// # true
/// }
///
/// const STYLED_STR: Styled<&str> = if is_black_on_white() {
/// ComboColorDisplay::<Black, White, _>::new("Hello").into_styled()
/// } else {
/// ComboColorDisplay::<White, Black, _>::new("Hello").into_styled()
/// };
///
/// println!("{}", STYLED_STR);
/// # assert_eq!(format!("{}", STYLED_STR), "\x1b[30;47mHello\x1b[0m");
/// ```
pub const fn into_styled(self) -> Styled<&'a T> {
let style = Style::new().fg::<Fg>().bg::<Bg>();
Styled { style, target: self.0 }
}
/// Set the background color at runtime. Only use if you do not know what color to use at
/// compile-time. If the color is constant, use either [`OwoColorize::bg`] or
/// a color-specific method, such as [`OwoColorize::on_yellow`],
///
/// ```rust
/// use owo_colors::{OwoColorize, AnsiColors};
///
/// println!("{}", "yellow background".on_color(AnsiColors::BrightYellow));
/// ```
pub const fn on_color<NewBg: DynColor>(
self,
bg: NewBg,
) -> ComboDynColorDisplay<'a, Fg::DynEquivalent, NewBg, T> {
ComboDynColorDisplay(self.0, Fg::DYN_EQUIVALENT, bg)
}
/// Set the foreground color at runtime. Only use if you do not know which color will be used at
/// compile-time. If the color is constant, use either [`OwoColorize::fg`] or
/// a color-specific method, such as [`OwoColorize::green`],
///
/// ```rust
/// use owo_colors::{OwoColorize, AnsiColors};
///
/// println!("{}", "green".color(AnsiColors::Green));
/// ```
pub const fn color<NewFg: DynColor>(
self,
fg: NewFg,
) -> ComboDynColorDisplay<'a, NewFg, Bg::DynEquivalent, T> {
ComboDynColorDisplay(self.0, fg, Bg::DYN_EQUIVALENT)
}
/// Set the foreground color generically
///
/// ```rust
/// use owo_colors::{OwoColorize, colors::*};
///
/// println!("{}", "red foreground".fg::<Red>());
/// ```
pub const fn fg<C: Color>(self) -> ComboColorDisplay<'a, C, Bg, T> {
ComboColorDisplay(self.0, PhantomData)
}
/// Set the background color generically.
///
/// ```rust
/// use owo_colors::{OwoColorize, colors::*};
///
/// println!("{}", "black background".bg::<Black>());
/// ```
pub const fn bg<C: Color>(self) -> ComboColorDisplay<'a, Fg, C, T> {
ComboColorDisplay(self.0, PhantomData)
}
$(
#[$bg_meta]
#[inline(always)]
pub const fn $bg_method(self) -> ComboColorDisplay<'a, Fg, colors::$color, T> {
ComboColorDisplay(self.0, PhantomData)
}
#[$fg_meta]
#[inline(always)]
pub const fn $fg_method(self) -> ComboColorDisplay<'a, colors::$color, Bg, T> {
ComboColorDisplay(self.0, PhantomData)
}
)*
}
};
}
const _: () = (); // workaround for syntax highlighting bug
color_methods! {
/// Change the foreground color to black
/// Change the background color to black
Black black on_black,
/// Change the foreground color to red
/// Change the background color to red
Red red on_red,
/// Change the foreground color to green
/// Change the background color to green
Green green on_green,
/// Change the foreground color to yellow
/// Change the background color to yellow
Yellow yellow on_yellow,
/// Change the foreground color to blue
/// Change the background color to blue
Blue blue on_blue,
/// Change the foreground color to magenta
/// Change the background color to magenta
Magenta magenta on_magenta,
/// Change the foreground color to purple
/// Change the background color to purple
Magenta purple on_purple,
/// Change the foreground color to cyan
/// Change the background color to cyan
Cyan cyan on_cyan,
/// Change the foreground color to white
/// Change the background color to white
White white on_white,
/// Change the foreground color to bright black
/// Change the background color to bright black
BrightBlack bright_black on_bright_black,
/// Change the foreground color to bright red
/// Change the background color to bright red
BrightRed bright_red on_bright_red,
/// Change the foreground color to bright green
/// Change the background color to bright green
BrightGreen bright_green on_bright_green,
/// Change the foreground color to bright yellow
/// Change the background color to bright yellow
BrightYellow bright_yellow on_bright_yellow,
/// Change the foreground color to bright blue
/// Change the background color to bright blue
BrightBlue bright_blue on_bright_blue,
/// Change the foreground color to bright magenta
/// Change the background color to bright magenta
BrightMagenta bright_magenta on_bright_magenta,
/// Change the foreground color to bright purple
/// Change the background color to bright purple
BrightMagenta bright_purple on_bright_purple,
/// Change the foreground color to bright cyan
/// Change the background color to bright cyan
BrightCyan bright_cyan on_bright_cyan,
/// Change the foreground color to bright white
/// Change the background color to bright white
BrightWhite bright_white on_bright_white,
}
impl<'a, Fg: DynColor + Copy, T: ?Sized> FgDynColorDisplay<'a, Fg, T> {
/// Create a new [`FgDynColorDisplay`], from a reference to a type which implements
/// [`DynColor`].
///
/// This is a const function: in non-const contexts, [`OwoColorize::color`] may be more
/// convenient.
///
/// # Example
///
/// Usage in const contexts:
///
/// ```rust
/// use owo_colors::{AnsiColors, FgDynColorDisplay};
///
/// const DYN_RED_TEXT: FgDynColorDisplay<AnsiColors, str> =
/// FgDynColorDisplay::new("red text (dynamic)", AnsiColors::Red);
///
/// println!("{}", DYN_RED_TEXT);
/// # assert_eq!(format!("{}", DYN_RED_TEXT), "\x1b[31mred text (dynamic)\x1b[39m");
/// ```
pub const fn new(thing: &'a T, color: Fg) -> Self {
Self(thing, color)
}
/// Convert self to a generic [`Styled`].
///
/// This method erases color-related type parameters, and can be
/// used to unify types across branches.
///
/// # Example
///
/// ```rust
/// use owo_colors::{AnsiColors, CssColors, OwoColorize};
///
/// fn is_blue() -> bool {
/// // ...
/// # true
/// }
///
/// let styled_str = if is_blue() {
/// "hello".color(AnsiColors::Blue).into_styled()
/// } else {
/// "hello".color(CssColors::DarkSeaGreen).into_styled()
/// };
///
/// println!("{}", styled_str);
/// # assert_eq!(format!("{}", styled_str), "\x1b[34mhello\x1b[0m");
/// ```
pub fn into_styled(self) -> Styled<&'a T> {
let Self(target, fg) = self;
let style = Style::new().color(fg);
Styled { style, target }
}
/// Set the background color at runtime. Only use if you do not know what color to use at
/// compile-time. If the color is constant, use either [`OwoColorize::bg`] or
/// a color-specific method, such as [`OwoColorize::on_yellow`],
///
/// ```rust
/// use owo_colors::{OwoColorize, AnsiColors};
///
/// println!("{}", "yellow background".on_color(AnsiColors::BrightYellow));
/// ```
pub const fn on_color<Bg: DynColor>(self, bg: Bg) -> ComboDynColorDisplay<'a, Fg, Bg, T> {
let Self(inner, fg) = self;
ComboDynColorDisplay(inner, fg, bg)
}
/// Set the foreground color at runtime. Only use if you do not know which color will be used at
/// compile-time. If the color is constant, use either [`OwoColorize::fg`] or
/// a color-specific method, such as [`OwoColorize::green`],
///
/// ```rust
/// use owo_colors::{OwoColorize, AnsiColors};
///
/// println!("{}", "green".color(AnsiColors::Green));
/// ```
pub const fn color<NewFg: DynColor>(self, fg: NewFg) -> FgDynColorDisplay<'a, NewFg, T> {
let Self(inner, _) = self;
FgDynColorDisplay(inner, fg)
}
}
impl<'a, Bg: DynColor + Copy, T: ?Sized> BgDynColorDisplay<'a, Bg, T> {
/// Create a new [`BgDynColorDisplay`], from a reference to a type which implements
/// [`DynColor`].
///
/// This is a const function: in non-const contexts, [`OwoColorize::on_color`] may be more
/// convenient.
///
/// # Example
///
/// Usage in const contexts:
///
/// ```rust
/// use owo_colors::{AnsiColors, BgDynColorDisplay};
///
/// const DYN_GREEN_BG_TEXT: BgDynColorDisplay<AnsiColors, str> =
/// BgDynColorDisplay::new("green background (dynamic)", AnsiColors::Green);
///
/// println!("{}", DYN_GREEN_BG_TEXT);
/// # assert_eq!(format!("{}", DYN_GREEN_BG_TEXT), "\x1b[42mgreen background (dynamic)\x1b[49m");
/// ```
pub const fn new(thing: &'a T, color: Bg) -> Self {
Self(thing, color)
}
/// Convert self to a generic [`Styled`].
///
/// This method erases color-related type parameters, and can be
/// used to unify types across branches.
///
/// # Example
///
/// ```rust
/// use owo_colors::{AnsiColors, CssColors, OwoColorize};
///
/// fn is_red() -> bool {
/// // ...
/// # true
/// }
///
/// let styled_str = if is_red() {
/// "hello".on_color(AnsiColors::Red).into_styled()
/// } else {
/// "hello".on_color(CssColors::LightGoldenRodYellow).into_styled()
/// };
///
/// println!("{}", styled_str);
/// # assert_eq!(format!("{}", styled_str), "\x1b[41mhello\x1b[0m");
/// ```
pub fn into_styled(self) -> Styled<&'a T> {
let Self(target, bg) = self;
let style = Style::new().on_color(bg);
Styled { style, target }
}
/// Set the background color at runtime. Only use if you do not know what color to use at
/// compile-time. If the color is constant, use either [`OwoColorize::bg`] or
/// a color-specific method, such as [`OwoColorize::on_yellow`],
///
/// ```rust
/// use owo_colors::{OwoColorize, AnsiColors};
///
/// println!("{}", "yellow background".on_color(AnsiColors::BrightYellow));
/// ```
pub const fn on_color<NewBg: DynColor>(self, bg: NewBg) -> BgDynColorDisplay<'a, NewBg, T> {
let Self(inner, _) = self;
BgDynColorDisplay(inner, bg)
}
/// Set the foreground color at runtime. Only use if you do not know which color will be used at
/// compile-time. If the color is constant, use either [`OwoColorize::fg`] or
/// a color-specific method, such as [`OwoColorize::green`],
///
/// ```rust
/// use owo_colors::{OwoColorize, AnsiColors};
///
/// println!("{}", "green".color(AnsiColors::Green));
/// ```
pub const fn color<Fg: DynColor>(self, fg: Fg) -> ComboDynColorDisplay<'a, Fg, Bg, T> {
let Self(inner, bg) = self;
ComboDynColorDisplay(inner, fg, bg)
}
}
impl<'a, Fg: DynColor + Copy, Bg: DynColor + Copy, T: ?Sized> ComboDynColorDisplay<'a, Fg, Bg, T> {
/// Create a new [`ComboDynColorDisplay`], from a pair of types which implement
/// [`DynColor`].
///
/// This is a const function: in non-const contexts, other functions may be more convenient.
///
/// # Example
///
/// Usage in const contexts:
///
/// ```rust
/// use owo_colors::{ComboDynColorDisplay, XtermColors};
///
/// const COMBO_DYN_TEXT: ComboDynColorDisplay<XtermColors, XtermColors, str> =
/// ComboDynColorDisplay::new(
/// "blue text on lilac background (dynamic)",
/// XtermColors::BlueRibbon,
/// XtermColors::WistfulLilac,
/// );
///
/// println!("{}", COMBO_DYN_TEXT);
/// # assert_eq!(format!("{}", COMBO_DYN_TEXT), "\x1b[38;5;27;48;5;146mblue text on lilac background (dynamic)\x1b[0m");
/// ```
pub const fn new(thing: &'a T, fg: Fg, bg: Bg) -> Self {
Self(thing, fg, bg)
}
/// Convert self to a generic [`Styled`].
///
/// This method erases color-related type parameters, and can be
/// used to unify types across branches.
///
/// # Example
///
/// Typical use:
///
/// ```rust
/// use owo_colors::{AnsiColors, CssColors, OwoColorize};
///
/// fn is_black_on_white() -> bool {
/// // ...
/// # true
/// }
///
/// let styled_str = if is_black_on_white() {
/// "hello".color(AnsiColors::Black).on_color(AnsiColors::White).into_styled()
/// } else {
/// "hello".color(CssColors::White).on_color(CssColors::Black).into_styled()
/// };
///
/// println!("{}", styled_str);
/// # assert_eq!(format!("{}", styled_str), "\x1b[30;47mhello\x1b[0m");
/// ```
pub fn into_styled(self) -> Styled<&'a T> {
let Self(target, fg, bg) = self;
let style = Style::new().color(fg).on_color(bg);
Styled { style, target }
}
/// Set the background color at runtime. Only use if you do not know what color to use at
/// compile-time. If the color is constant, use either [`OwoColorize::bg`] or
/// a color-specific method, such as [`OwoColorize::on_yellow`],
///
/// ```rust
/// use owo_colors::{OwoColorize, AnsiColors};
///
/// println!("{}", "yellow background".on_color(AnsiColors::BrightYellow));
/// ```
pub const fn on_color<NewBg: DynColor>(
self,
bg: NewBg,
) -> ComboDynColorDisplay<'a, Fg, NewBg, T> {
let Self(inner, fg, _) = self;
ComboDynColorDisplay(inner, fg, bg)
}
/// Set the foreground color at runtime. Only use if you do not know which color will be used at
/// compile-time. If the color is constant, use either [`OwoColorize::fg`] or
/// a color-specific method, such as [`OwoColorize::green`],
///
/// ```rust
/// use owo_colors::{OwoColorize, AnsiColors};
///
/// println!("{}", "green".color(AnsiColors::Green));
/// ```
pub const fn color<NewFg: DynColor>(self, fg: NewFg) -> ComboDynColorDisplay<'a, NewFg, Bg, T> {
// TODO: Make this const after https://github.com/rust-lang/rust/issues/73255 is stabilized.
let Self(inner, _, bg) = self;
ComboDynColorDisplay(inner, fg, bg)
}
}
#[cfg(test)]
mod tests {
use crate::{AnsiColors, OwoColorize, colors::*};
#[test]
fn fg_bg_combo() {
let test = "test".red().on_blue();
assert_eq!(test.to_string(), "\x1b[31;44mtest\x1b[0m");
}
#[test]
fn bg_fg_combo() {
let test = "test".on_blue().red();
assert_eq!(test.to_string(), "\x1b[31;44mtest\x1b[0m");
}
#[test]
fn fg_bg_dyn_combo() {
let test = "test".color(AnsiColors::Red).on_color(AnsiColors::Blue);
assert_eq!(test.to_string(), "\x1b[31;44mtest\x1b[0m");
}
#[test]
fn bg_fg_dyn_combo() {
let test = "test".on_color(AnsiColors::Blue).color(AnsiColors::Red);
assert_eq!(test.to_string(), "\x1b[31;44mtest\x1b[0m");
}
#[test]
fn fg_override() {
let test = "test".green().yellow().red().on_blue();
assert_eq!(test.to_string(), "\x1b[31;44mtest\x1b[0m");
}
#[test]
fn bg_override() {
let test = "test".on_green().on_yellow().on_blue().red();
assert_eq!(test.to_string(), "\x1b[31;44mtest\x1b[0m");
}
#[test]
fn multiple_override() {
let test = "test"
.on_green()
.on_yellow()
.on_red()
.green()
.on_blue()
.red();
assert_eq!(test.to_string(), "\x1b[31;44mtest\x1b[0m");
let test = "test"
.color(AnsiColors::Blue)
.color(AnsiColors::White)
.on_color(AnsiColors::Black)
.color(AnsiColors::Red)
.on_color(AnsiColors::Blue);
assert_eq!(test.to_string(), "\x1b[31;44mtest\x1b[0m");
let test = "test"
.on_yellow()
.on_red()
.on_color(AnsiColors::Black)
.color(AnsiColors::Red)
.on_color(AnsiColors::Blue);
assert_eq!(test.to_string(), "\x1b[31;44mtest\x1b[0m");
let test = "test"
.yellow()
.red()
.color(AnsiColors::Red)
.on_color(AnsiColors::Black)
.on_color(AnsiColors::Blue);
assert_eq!(test.to_string(), "\x1b[31;44mtest\x1b[0m");
let test = "test"
.yellow()
.red()
.on_color(AnsiColors::Black)
.color(AnsiColors::Red)
.on_color(AnsiColors::Blue);
assert_eq!(test.to_string(), "\x1b[31;44mtest\x1b[0m");
}
#[test]
fn generic_multiple_override() {
use crate::colors::*;
let test = "test"
.bg::<Green>()
.bg::<Yellow>()
.bg::<Red>()
.fg::<Green>()
.bg::<Blue>()
.fg::<Red>();
assert_eq!(test.to_string(), "\x1b[31;44mtest\x1b[0m");
}
#[test]
fn fg_bg_combo_generic() {
let test = "test".fg::<Red>().bg::<Blue>();
assert_eq!(test.to_string(), "\x1b[31;44mtest\x1b[0m");
}
#[test]
fn bg_fg_combo_generic() {
let test = "test".bg::<Blue>().fg::<Red>();
assert_eq!(test.to_string(), "\x1b[31;44mtest\x1b[0m");
}
}
+115
View File
@@ -0,0 +1,115 @@
#[allow(unused_imports)]
use crate::{
AnsiColors, BgDynColorDisplay, CssColors, DynColor, FgDynColorDisplay, Rgb, XtermColors,
};
use core::fmt;
/// An enum describing runtime-configurable colors
///
/// This can be displayed using [`FgDynColorDisplay`](FgDynColorDisplay) or [`BgDynColorDisplay`](BgDynColorDisplay),
/// allowing for multiple types of colors to be used at runtime.
#[allow(missing_docs)]
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum DynColors {
Ansi(AnsiColors),
Css(CssColors),
Xterm(XtermColors),
Rgb(u8, u8, u8),
}
impl crate::private::Sealed for DynColors {}
impl DynColor for DynColors {
fn fmt_ansi_fg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DynColors::Ansi(ansi) => ansi.fmt_ansi_fg(f),
DynColors::Css(css) => css.fmt_ansi_fg(f),
DynColors::Xterm(xterm) => xterm.fmt_ansi_fg(f),
&DynColors::Rgb(r, g, b) => Rgb(r, g, b).fmt_ansi_fg(f),
}
}
fn fmt_ansi_bg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DynColors::Ansi(ansi) => ansi.fmt_ansi_bg(f),
DynColors::Css(css) => css.fmt_ansi_bg(f),
DynColors::Xterm(xterm) => xterm.fmt_ansi_bg(f),
&DynColors::Rgb(r, g, b) => Rgb(r, g, b).fmt_ansi_bg(f),
}
}
fn fmt_raw_ansi_fg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DynColors::Ansi(ansi) => ansi.fmt_raw_ansi_fg(f),
DynColors::Css(css) => css.fmt_raw_ansi_fg(f),
DynColors::Xterm(xterm) => xterm.fmt_raw_ansi_fg(f),
&DynColors::Rgb(r, g, b) => Rgb(r, g, b).fmt_raw_ansi_fg(f),
}
}
fn fmt_raw_ansi_bg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DynColors::Ansi(ansi) => ansi.fmt_raw_ansi_bg(f),
DynColors::Css(css) => css.fmt_raw_ansi_bg(f),
DynColors::Xterm(xterm) => xterm.fmt_raw_ansi_bg(f),
&DynColors::Rgb(r, g, b) => Rgb(r, g, b).fmt_raw_ansi_bg(f),
}
}
#[doc(hidden)]
fn get_dyncolors_fg(&self) -> crate::DynColors {
*self
}
#[doc(hidden)]
fn get_dyncolors_bg(&self) -> crate::DynColors {
*self
}
}
/// An error for when the color can not be parsed from a string at runtime
#[derive(Debug)]
pub struct ParseColorError;
impl core::str::FromStr for DynColors {
type Err = ParseColorError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.chars().next().ok_or(ParseColorError)? == '#' {
match s.len() {
4 => {
// TODO
Err(ParseColorError)
}
7 => Ok(Self::Rgb(
u8::from_str_radix(&s[1..3], 16).or(Err(ParseColorError))?,
u8::from_str_radix(&s[3..5], 16).or(Err(ParseColorError))?,
u8::from_str_radix(&s[5..7], 16).or(Err(ParseColorError))?,
)),
_ => Err(ParseColorError),
}
} else {
let ansi = match s {
"black" => AnsiColors::Black,
"red" => AnsiColors::Red,
"green" => AnsiColors::Green,
"yellow" => AnsiColors::Yellow,
"blue" => AnsiColors::Blue,
"magenta" | "purple" => AnsiColors::Magenta,
"cyan" => AnsiColors::Cyan,
"white" => AnsiColors::White,
"bright black" => AnsiColors::BrightBlack,
"bright red" => AnsiColors::BrightRed,
"bright green" => AnsiColors::BrightGreen,
"bright yellow" => AnsiColors::BrightYellow,
"bright blue" => AnsiColors::BrightBlue,
"bright magenta" => AnsiColors::BrightMagenta,
"bright cyan" => AnsiColors::BrightCyan,
"bright white" => AnsiColors::BrightWhite,
_ => return Err(ParseColorError),
};
Ok(Self::Ansi(ansi))
}
}
}
+797
View File
@@ -0,0 +1,797 @@
use crate::{AnsiColors, Color, DynColor, DynColors};
use core::fmt;
#[cfg(doc)]
use crate::OwoColorize;
/// A runtime-configurable text effect for use with [`Style`]
#[allow(missing_docs)]
#[derive(Debug, Copy, Clone)]
pub enum Effect {
Bold,
Dimmed,
Italic,
Underline,
Blink,
BlinkFast,
Reversed,
Hidden,
Strikethrough,
}
macro_rules! color_methods {
($(
#[$fg_meta:meta] #[$bg_meta:meta] $color:ident $fg_method:ident $bg_method:ident
),* $(,)?) => {
$(
#[$fg_meta]
#[must_use]
pub const fn $fg_method(mut self) -> Self {
self.fg = Some(DynColors::Ansi(AnsiColors::$color));
self
}
#[$fg_meta]
#[must_use]
pub const fn $bg_method(mut self) -> Self {
self.bg = Some(DynColors::Ansi(AnsiColors::$color));
self
}
)*
};
}
macro_rules! style_methods {
($(#[$meta:meta] ($name:ident, $set_name:ident)),* $(,)?) => {
$(
#[$meta]
#[must_use]
pub const fn $name(mut self) -> Self {
self.style_flags = self.style_flags.$set_name(true);
self
}
)*
};
}
const _: () = (); // workaround for syntax highlighting bug
/// A wrapper type which applies a [`Style`] when displaying the inner type
pub struct Styled<T> {
/// The target value to be styled
pub(crate) target: T,
/// The style to apply to target
pub style: Style,
}
/// A pre-computed style that can be applied to a struct using [`OwoColorize::style`].
///
/// Its interface mimics that of [`OwoColorize`], but instead of chaining methods on your
/// object, you instead chain them on the `Style` object before applying it.
///
/// ```rust
/// use owo_colors::{OwoColorize, Style};
///
/// let my_style = Style::new()
/// .red()
/// .on_white()
/// .strikethrough();
///
/// println!("{}", "red text, white background, struck through".style(my_style));
/// ```
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Style {
pub(crate) fg: Option<DynColors>,
pub(crate) bg: Option<DynColors>,
pub(crate) bold: bool,
pub(crate) style_flags: StyleFlags,
}
#[repr(transparent)]
#[derive(Debug, Copy, Clone, PartialEq)]
pub(crate) struct StyleFlags(pub(crate) u8);
impl StyleFlags {
#[must_use]
#[inline]
const fn is_plain(&self) -> bool {
self.0 == 0
}
}
const DIMMED_SHIFT: u8 = 0;
const ITALIC_SHIFT: u8 = 1;
const UNDERLINE_SHIFT: u8 = 2;
const BLINK_SHIFT: u8 = 3;
const BLINK_FAST_SHIFT: u8 = 4;
const REVERSED_SHIFT: u8 = 5;
const HIDDEN_SHIFT: u8 = 6;
const STRIKETHROUGH_SHIFT: u8 = 7;
macro_rules! style_flags_methods {
($(($shift:ident, $name:ident, $set_name:ident)),* $(,)?) => {
$(
#[must_use]
const fn $name(&self) -> bool {
((self.0 >> $shift) & 1) != 0
}
#[must_use]
const fn $set_name(mut self, $name: bool) -> Self {
self.0 = (self.0 & !(1 << $shift)) | (($name as u8) << $shift);
self
}
)*
};
}
impl StyleFlags {
const fn new() -> Self {
Self(0)
}
style_flags_methods! {
(DIMMED_SHIFT, dimmed, set_dimmed),
(ITALIC_SHIFT, italic, set_italic),
(UNDERLINE_SHIFT, underline, set_underline),
(BLINK_SHIFT, blink, set_blink),
(BLINK_FAST_SHIFT, blink_fast, set_blink_fast),
(REVERSED_SHIFT, reversed, set_reversed),
(HIDDEN_SHIFT, hidden, set_hidden),
(STRIKETHROUGH_SHIFT, strikethrough, set_strikethrough),
}
}
impl Default for StyleFlags {
fn default() -> Self {
Self::new()
}
}
impl Style {
/// Create a new style to be applied later
#[must_use]
pub const fn new() -> Self {
Self {
fg: None,
bg: None,
bold: false,
style_flags: StyleFlags::new(),
}
}
/// Apply the style to a given struct to output.
///
/// # Example
///
/// Usage in const contexts:
///
/// ```rust
/// use owo_colors::{OwoColorize, Style, Styled};
///
/// const STYLED_TEXT: Styled<&'static str> = Style::new().bold().italic().style("bold and italic text");
///
/// println!("{}", STYLED_TEXT);
/// # assert_eq!(format!("{}", STYLED_TEXT), "\u{1b}[1;3mbold and italic text\u{1b}[0m");
/// ```
pub const fn style<T>(&self, target: T) -> Styled<T> {
Styled {
target,
style: *self,
}
}
/// Set the foreground color generically
///
/// ```rust
/// use owo_colors::{OwoColorize, colors::*};
///
/// println!("{}", "red foreground".fg::<Red>());
/// ```
#[must_use]
pub const fn fg<C: Color>(mut self) -> Self {
self.fg = Some(C::DYN_COLORS_EQUIVALENT);
self
}
/// Set the background color generically.
///
/// ```rust
/// use owo_colors::{OwoColorize, colors::*};
///
/// println!("{}", "black background".bg::<Black>());
/// ```
#[must_use]
pub const fn bg<C: Color>(mut self) -> Self {
self.bg = Some(C::DYN_COLORS_EQUIVALENT);
self
}
/// Removes the foreground color from the style. Note that this does not apply
/// the default color, but rather represents not changing the current terminal color.
///
/// If you wish to actively change the terminal color back to the default, see
/// [`Style::default_color`].
#[must_use]
pub const fn remove_fg(mut self) -> Self {
self.fg = None;
self
}
/// Removes the background color from the style. Note that this does not apply
/// the default color, but rather represents not changing the current terminal color.
///
/// If you wish to actively change the terminal color back to the default, see
/// [`Style::on_default_color`].
#[must_use]
pub const fn remove_bg(mut self) -> Self {
self.bg = None;
self
}
color_methods! {
/// Change the foreground color to black
/// Change the background color to black
Black black on_black,
/// Change the foreground color to red
/// Change the background color to red
Red red on_red,
/// Change the foreground color to green
/// Change the background color to green
Green green on_green,
/// Change the foreground color to yellow
/// Change the background color to yellow
Yellow yellow on_yellow,
/// Change the foreground color to blue
/// Change the background color to blue
Blue blue on_blue,
/// Change the foreground color to magenta
/// Change the background color to magenta
Magenta magenta on_magenta,
/// Change the foreground color to purple
/// Change the background color to purple
Magenta purple on_purple,
/// Change the foreground color to cyan
/// Change the background color to cyan
Cyan cyan on_cyan,
/// Change the foreground color to white
/// Change the background color to white
White white on_white,
/// Change the foreground color to the terminal default
/// Change the background color to the terminal default
Default default_color on_default_color,
/// Change the foreground color to bright black
/// Change the background color to bright black
BrightBlack bright_black on_bright_black,
/// Change the foreground color to bright red
/// Change the background color to bright red
BrightRed bright_red on_bright_red,
/// Change the foreground color to bright green
/// Change the background color to bright green
BrightGreen bright_green on_bright_green,
/// Change the foreground color to bright yellow
/// Change the background color to bright yellow
BrightYellow bright_yellow on_bright_yellow,
/// Change the foreground color to bright blue
/// Change the background color to bright blue
BrightBlue bright_blue on_bright_blue,
/// Change the foreground color to bright magenta
/// Change the background color to bright magenta
BrightMagenta bright_magenta on_bright_magenta,
/// Change the foreground color to bright purple
/// Change the background color to bright purple
BrightMagenta bright_purple on_bright_purple,
/// Change the foreground color to bright cyan
/// Change the background color to bright cyan
BrightCyan bright_cyan on_bright_cyan,
/// Change the foreground color to bright white
/// Change the background color to bright white
BrightWhite bright_white on_bright_white,
}
/// Make the text bold
#[must_use]
pub const fn bold(mut self) -> Self {
self.bold = true;
self
}
style_methods! {
/// Make the text dim
(dimmed, set_dimmed),
/// Make the text italicized
(italic, set_italic),
/// Make the text underlined
(underline, set_underline),
/// Make the text blink
(blink, set_blink),
/// Make the text blink (but fast!)
(blink_fast, set_blink_fast),
/// Swap the foreground and background colors
(reversed, set_reversed),
/// Hide the text
(hidden, set_hidden),
/// Cross out the text
(strikethrough, set_strikethrough),
}
#[must_use]
const fn set_effect(mut self, effect: Effect, to: bool) -> Self {
use Effect::*;
match effect {
Bold => {
self.bold = to;
}
Dimmed => {
// This somewhat contorted construction is required because const fns can't take
// mutable refs as of Rust 1.81.
self.style_flags = self.style_flags.set_dimmed(to);
}
Italic => {
self.style_flags = self.style_flags.set_italic(to);
}
Underline => {
self.style_flags = self.style_flags.set_underline(to);
}
Blink => {
self.style_flags = self.style_flags.set_blink(to);
}
BlinkFast => {
self.style_flags = self.style_flags.set_blink_fast(to);
}
Reversed => {
self.style_flags = self.style_flags.set_reversed(to);
}
Hidden => {
self.style_flags = self.style_flags.set_hidden(to);
}
Strikethrough => {
self.style_flags = self.style_flags.set_strikethrough(to);
}
}
self
}
#[must_use]
const fn set_effects(mut self, mut effects: &[Effect], to: bool) -> Self {
// This is basically a for loop that also works in const contexts.
while let [first, rest @ ..] = effects {
self = self.set_effect(*first, to);
effects = rest;
}
self
}
/// Apply a given effect from the style
#[must_use]
pub const fn effect(self, effect: Effect) -> Self {
self.set_effect(effect, true)
}
/// Remove a given effect from the style
#[must_use]
pub const fn remove_effect(self, effect: Effect) -> Self {
self.set_effect(effect, false)
}
/// Apply a given set of effects to the style
#[must_use]
pub const fn effects(self, effects: &[Effect]) -> Self {
self.set_effects(effects, true)
}
/// Remove a given set of effects from the style
#[must_use]
pub const fn remove_effects(self, effects: &[Effect]) -> Self {
self.set_effects(effects, false)
}
/// Disables all the given effects from the style
#[must_use]
pub const fn remove_all_effects(mut self) -> Self {
self.bold = false;
self.style_flags = StyleFlags::new();
self
}
/// Set the foreground color at runtime. Only use if you do not know which color will be used at
/// compile-time. If the color is constant, use either [`OwoColorize::fg`](crate::OwoColorize::fg) or
/// a color-specific method, such as [`OwoColorize::green`](crate::OwoColorize::green),
///
/// ```rust
/// use owo_colors::{OwoColorize, AnsiColors};
///
/// println!("{}", "green".color(AnsiColors::Green));
/// ```
#[must_use]
pub fn color<Color: DynColor>(mut self, color: Color) -> Self {
// Can't be const because `get_dyncolors_fg` is a trait method.
self.fg = Some(color.get_dyncolors_fg());
self
}
/// Set the background color at runtime. Only use if you do not know what color to use at
/// compile-time. If the color is constant, use either [`OwoColorize::bg`](crate::OwoColorize::bg) or
/// a color-specific method, such as [`OwoColorize::on_yellow`](crate::OwoColorize::on_yellow),
///
/// ```rust
/// use owo_colors::{OwoColorize, AnsiColors};
///
/// println!("{}", "yellow background".on_color(AnsiColors::BrightYellow));
/// ```
#[must_use]
pub fn on_color<Color: DynColor>(mut self, color: Color) -> Self {
// Can't be const because `get_dyncolors_bg` is a trait method.
self.bg = Some(color.get_dyncolors_bg());
self
}
/// Set the foreground color to a specific RGB value.
#[must_use]
pub const fn fg_rgb<const R: u8, const G: u8, const B: u8>(mut self) -> Self {
self.fg = Some(DynColors::Rgb(R, G, B));
self
}
/// Set the background color to a specific RGB value.
#[must_use]
pub const fn bg_rgb<const R: u8, const G: u8, const B: u8>(mut self) -> Self {
self.bg = Some(DynColors::Rgb(R, G, B));
self
}
/// Sets the foreground color to an RGB value.
#[must_use]
pub const fn truecolor(mut self, r: u8, g: u8, b: u8) -> Self {
self.fg = Some(DynColors::Rgb(r, g, b));
self
}
/// Sets the background color to an RGB value.
#[must_use]
pub const fn on_truecolor(mut self, r: u8, g: u8, b: u8) -> Self {
self.bg = Some(DynColors::Rgb(r, g, b));
self
}
/// Returns true if the style does not apply any formatting.
#[must_use]
#[inline]
pub const fn is_plain(&self) -> bool {
let s = &self;
!(s.fg.is_some() || s.bg.is_some() || s.bold) && s.style_flags.is_plain()
}
/// Returns a formatter for the style's ANSI prefix.
///
/// This can be used to separate out the prefix and suffix of a style.
///
/// # Example
///
/// ```
/// use owo_colors::Style;
/// use std::fmt::Write;
///
/// let style = Style::new().red().on_blue();
/// let prefix = style.prefix_formatter();
/// let suffix = style.suffix_formatter();
///
/// // Write the prefix and suffix separately.
/// let mut output = String::new();
/// write!(output, "{}", prefix);
/// output.push_str("Hello");
/// write!(output, "{}", suffix);
///
/// assert_eq!(output, "\x1b[31;44mHello\x1b[0m");
/// ```
pub const fn prefix_formatter(&self) -> StylePrefixFormatter {
StylePrefixFormatter(*self)
}
/// Returns a formatter for the style's ANSI suffix.
///
/// This can be used to separate out the prefix and suffix of a style.
///
/// # Example
///
/// See [`Style::prefix_formatter`].
pub const fn suffix_formatter(&self) -> StyleSuffixFormatter {
StyleSuffixFormatter(*self)
}
/// Applies the ANSI-prefix for this style to the given formatter
#[inline]
#[allow(unused_assignments)]
pub fn fmt_prefix(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = self;
let format_less_important_effects = s.style_flags != StyleFlags::default();
let format_effect = s.bold || format_less_important_effects;
let format_any = !self.is_plain();
let mut semicolon = false;
if format_any {
f.write_str("\x1b[")?;
}
if let Some(fg) = s.fg {
<DynColors as DynColor>::fmt_raw_ansi_fg(&fg, f)?;
semicolon = true;
}
if let Some(bg) = s.bg {
if s.fg.is_some() {
f.write_str(";")?;
}
<DynColors as DynColor>::fmt_raw_ansi_bg(&bg, f)?;
semicolon = true;
}
if format_effect {
if s.bold {
if semicolon {
f.write_str(";")?;
}
f.write_str("1")?;
semicolon = true;
}
macro_rules! text_effect_fmt {
($style:ident, $formatter:ident, $semicolon:ident, $(($attr:ident, $value:literal)),* $(,)?) => {
$(
if $style.style_flags.$attr() {
if $semicolon {
$formatter.write_str(";")?;
}
$formatter.write_str($value)?;
$semicolon = true;
}
)+
}
}
if format_less_important_effects {
text_effect_fmt! {
s, f, semicolon,
(dimmed, "2"),
(italic, "3"),
(underline, "4"),
(blink, "5"),
(blink_fast, "6"),
(reversed, "7"),
(hidden, "8"),
(strikethrough, "9"),
}
}
}
if format_any {
f.write_str("m")?;
}
Ok(())
}
/// Applies the ANSI-suffix for this style to the given formatter
#[inline]
pub fn fmt_suffix(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if !self.is_plain() {
f.write_str("\x1b[0m")?;
}
Ok(())
}
}
/// Formatter for the prefix of a [`Style`].
///
/// This is used to get the ANSI escape codes for the style without
/// the suffix, which is useful for formatting the prefix separately.
#[derive(Debug, Clone, Copy, PartialEq)]
#[must_use = "this formatter does nothing unless displayed"]
pub struct StylePrefixFormatter(Style);
impl fmt::Display for StylePrefixFormatter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt_prefix(f)
}
}
/// Formatter for the suffix of a [`Style`].
///
/// This is used to get the ANSI escape codes for the style without
/// the prefix, which is useful for formatting the suffix separately.
#[derive(Debug, Clone, Copy, PartialEq)]
#[must_use = "this formatter does nothing unless displayed"]
pub struct StyleSuffixFormatter(Style);
impl fmt::Display for StyleSuffixFormatter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt_suffix(f)
}
}
impl Default for Style {
fn default() -> Self {
Self::new()
}
}
/// Helper to create [`Style`]s more ergonomically
pub const fn style() -> Style {
Style::new()
}
impl<T> Styled<T> {
/// Returns a reference to the inner value to be styled
pub const fn inner(&self) -> &T {
&self.target
}
/// Returns a mutable reference to the inner value to be styled.
///
/// *This method is const on Rust 1.83+.*
#[cfg(const_mut_refs)]
pub const fn inner_mut(&mut self) -> &mut T {
&mut self.target
}
/// Returns a mutable reference to the inner value to be styled.
///
/// *This method is const on Rust 1.83+.*
#[cfg(not(const_mut_refs))]
pub fn inner_mut(&mut self) -> &mut T {
&mut self.target
}
}
macro_rules! impl_fmt {
($($trait:path),* $(,)?) => {
$(
impl<T: $trait> $trait for Styled<T> {
#[allow(unused_assignments)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.style.fmt_prefix(f)?;
<T as $trait>::fmt(&self.target, f)?;
self.style.fmt_suffix(f)
}
}
)*
};
}
impl_fmt! {
fmt::Display,
fmt::Debug,
fmt::UpperHex,
fmt::LowerHex,
fmt::Binary,
fmt::UpperExp,
fmt::LowerExp,
fmt::Octal,
fmt::Pointer,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{AnsiColors, OwoColorize};
#[test]
fn size_of() {
let size = std::mem::size_of::<Style>();
assert_eq!(size, 10, "size of Style should be 10 bytes");
}
#[test]
fn test_it() {
let style = Style::new()
.bright_white()
.on_blue()
.bold()
.dimmed()
.italic()
.underline()
.blink()
//.blink_fast()
//.reversed()
//.hidden()
.strikethrough();
let s = style.style("TEST");
let s2 = format!("{}", &s);
println!("{}", &s2);
assert_eq!(&s2, "\u{1b}[97;44;1;2;3;4;5;9mTEST\u{1b}[0m");
let prefix = format!("{}", style.prefix_formatter());
assert_eq!(&prefix, "\u{1b}[97;44;1;2;3;4;5;9m");
let suffix = format!("{}", style.suffix_formatter());
assert_eq!(&suffix, "\u{1b}[0m");
}
#[test]
fn test_effects() {
use Effect::*;
let style = Style::new().effects(&[Strikethrough, Underline]);
let s = style.style("TEST");
let s2 = format!("{}", &s);
println!("{}", &s2);
assert_eq!(&s2, "\u{1b}[4;9mTEST\u{1b}[0m");
}
#[test]
fn test_color() {
let style = Style::new()
.color(AnsiColors::White)
.on_color(AnsiColors::Black);
let s = style.style("TEST");
let s2 = format!("{}", &s);
println!("{}", &s2);
assert_eq!(&s2, "\u{1b}[37;40mTEST\u{1b}[0m");
}
#[test]
fn test_truecolor() {
let style = Style::new().truecolor(255, 255, 255).on_truecolor(0, 0, 0);
let s = style.style("TEST");
let s2 = format!("{}", &s);
println!("{}", &s2);
assert_eq!(&s2, "\u{1b}[38;2;255;255;255;48;2;0;0;0mTEST\u{1b}[0m");
}
#[test]
fn test_string_reference() {
let style = Style::new().truecolor(255, 255, 255).on_truecolor(0, 0, 0);
let string = String::from("TEST");
let s = style.style(&string);
let s2 = format!("{}", &s);
println!("{}", &s2);
assert_eq!(&s2, "\u{1b}[38;2;255;255;255;48;2;0;0;0mTEST\u{1b}[0m");
}
#[test]
fn test_owocolorize() {
let style = Style::new().bright_white().on_blue();
let s = "TEST".style(style);
let s2 = format!("{}", &s);
println!("{}", &s2);
assert_eq!(&s2, "\u{1b}[97;44mTEST\u{1b}[0m");
}
#[test]
fn test_is_plain() {
let style = Style::new().bright_white().on_blue();
assert!(!style.is_plain());
assert!(Style::default().is_plain());
let string = String::from("TEST");
let s = Style::default().style(&string);
let s2 = format!("{}", &s);
assert_eq!(string, s2)
}
#[test]
fn test_inner() {
let style = Style::default();
let mut s = "TEST".style(style);
assert_eq!(&&"TEST", s.inner());
*s.inner_mut() = &"changed";
assert_eq!(&&"changed", s.inner());
assert_eq!("changed", format!("{}", s));
}
}
+557
View File
@@ -0,0 +1,557 @@
//! |**Quick Links**|[`OwoColorize`]|[`Style`]|[`StyledList`]|[`github`](https://github.com/owo-colors/owo-colors)|
//! |-|-|-|-|-|
//!
//! ---
//!
//! This crate provides [`OwoColorize`], an extension trait for colorizing a
//! given type.
//!
//! ## Example
//!
//! ```rust
//! use owo_colors::OwoColorize;
//!
//! // Foreground colors
//! println!("My number is {:#x}!", 10.green());
//! // Background colors
//! println!("My number is not {}!", 4.on_red());
//! ```
//!
//! ## Generically color
//!
//! ```rust
//! use owo_colors::OwoColorize;
//! use owo_colors::colors::*;
//!
//! // Generically color
//! println!("My number might be {}!", 4.fg::<Black>().bg::<Yellow>());
//! ```
//!
//! ## Stylize
//!
//! ```rust
//! use owo_colors::OwoColorize;
//!
//! println!("{}", "strikethrough".strikethrough());
//! ```
//!
//! ## Only Style on Supported Terminals
//!
//! ```rust
//! # #[cfg(feature = "supports-color")] {
//! use owo_colors::{OwoColorize, Stream::Stdout};
//!
//! println!(
//! "{}",
//! "colored blue if a supported terminal"
//! .if_supports_color(Stdout, |text| text.bright_blue())
//! );
//! # }
//! ```
//!
//! Supports `NO_COLOR`/`FORCE_COLOR` environment variables, checks if it's a tty, checks
//! if it's running in CI (and thus likely supports color), and checks which terminal is being
//! used. (Note: requires `supports-colors` feature)
//!
//! ## Style Objects
//!
//! owo-colors also features the ability to create a [`Style`] object and use it to
//! apply the same set of colors/effects to any number of things to display.
//!
//! ```rust
//! use owo_colors::{OwoColorize, Style};
//!
//! let my_style = Style::new()
//! .red()
//! .on_white()
//! .strikethrough();
//!
//! let text = "red text, white background, struck through";
//! println!("{}", text.style(my_style));
//! ```
#![cfg_attr(not(test), no_std)]
#![cfg_attr(doc_cfg, feature(doc_cfg))]
#![doc(html_logo_url = "https://jam1.re/img/rust_owo.svg")]
#![warn(missing_docs)]
#![forbid(unsafe_code)]
pub mod colors;
mod combo;
mod dyn_colors;
mod dyn_styles;
mod styled_list;
pub mod styles;
#[cfg(feature = "supports-colors")]
mod overrides;
#[cfg(feature = "supports-colors")]
pub(crate) use overrides::OVERRIDE;
use core::fmt;
use core::marker::PhantomData;
mod private {
// Not actually reachable.
#[doc(hidden)]
pub trait Sealed {}
}
/// A trait for describing a type which can be used with [`FgColorDisplay`] or
/// [`BgColorDisplay`]
pub trait Color: private::Sealed {
/// The ANSI format code for setting this color as the foreground
const ANSI_FG: &'static str;
/// The ANSI format code for setting this color as the background
const ANSI_BG: &'static str;
/// The raw ANSI format for settings this color as the foreground without the ANSI
/// delimiters ("\x1b" and "m")
const RAW_ANSI_FG: &'static str;
/// The raw ANSI format for settings this color as the background without the ANSI
/// delimiters ("\x1b" and "m")
const RAW_ANSI_BG: &'static str;
#[doc(hidden)]
type DynEquivalent: DynColor;
#[doc(hidden)]
const DYN_EQUIVALENT: Self::DynEquivalent;
#[doc(hidden)]
const DYN_COLORS_EQUIVALENT: crate::DynColors;
}
/// A trait describing a runtime-configurable color which can displayed using [`FgDynColorDisplay`]
/// or [`BgDynColorDisplay`]. If your color will be known at compile time it
/// is recommended you avoid this.
pub trait DynColor: private::Sealed {
/// A function to output a ANSI code to a formatter to set the foreground to this color
fn fmt_ansi_fg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result;
/// A function to output a ANSI code to a formatter to set the background to this color
fn fmt_ansi_bg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result;
/// A function to output a raw ANSI code to a formatter to set the foreground to this color,
/// but without including the ANSI delimiters.
fn fmt_raw_ansi_fg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result;
/// A function to output a raw ANSI code to a formatter to set the background to this color,
/// but without including the ANSI delimiters.
fn fmt_raw_ansi_bg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result;
#[doc(hidden)]
fn get_dyncolors_fg(&self) -> DynColors;
#[doc(hidden)]
fn get_dyncolors_bg(&self) -> DynColors;
}
/// Transparent wrapper around a type which implements all the formatters the wrapped type does,
/// with the addition of changing the foreground color. Recommended to be constructed using
/// [`OwoColorize`].
#[repr(transparent)]
pub struct FgColorDisplay<'a, C: Color, T: ?Sized>(&'a T, PhantomData<C>);
/// Transparent wrapper around a type which implements all the formatters the wrapped type does,
/// with the addition of changing the background color. Recommended to be constructed using
/// [`OwoColorize`].
#[repr(transparent)]
pub struct BgColorDisplay<'a, C: Color, T: ?Sized>(&'a T, PhantomData<C>);
/// Wrapper around a type which implements all the formatters the wrapped type does,
/// with the addition of changing the foreground color. Is not recommended unless compile-time
/// coloring is not an option.
pub struct FgDynColorDisplay<'a, Color: DynColor, T: ?Sized>(&'a T, Color);
/// Wrapper around a type which implements all the formatters the wrapped type does,
/// with the addition of changing the background color. Is not recommended unless compile-time
/// coloring is not an option.
pub struct BgDynColorDisplay<'a, Color: DynColor, T: ?Sized>(&'a T, Color);
macro_rules! style_methods {
($(#[$meta:meta] $name:ident $ty:ident),* $(,)?) => {
$(
#[$meta]
#[must_use]
#[inline(always)]
#[rust_analyzer::completions(ignore_flyimport)]
fn $name(&self) -> styles::$ty<'_, Self> {
styles::$ty(self)
}
)*
};
}
const _: () = (); // workaround for syntax highlighting bug
macro_rules! color_methods {
($(
#[$fg_meta:meta] #[$bg_meta:meta] $color:ident $fg_method:ident $bg_method:ident
),* $(,)?) => {
$(
#[$fg_meta]
#[must_use]
#[inline(always)]
#[rust_analyzer::completions(ignore_flyimport)]
fn $fg_method(&self) -> FgColorDisplay<'_, colors::$color, Self> {
FgColorDisplay(self, PhantomData)
}
#[$bg_meta]
#[must_use]
#[inline(always)]
#[rust_analyzer::completions(ignore_flyimport)]
fn $bg_method(&self) -> BgColorDisplay<'_, colors::$color, Self> {
BgColorDisplay(self, PhantomData)
}
)*
};
}
const _: () = (); // workaround for syntax highlighting bug
/// Extension trait for colorizing a type which implements any std formatter
/// ([`Display`](core::fmt::Display), [`Debug`](core::fmt::Debug), [`UpperHex`](core::fmt::UpperHex),
/// etc.)
///
/// ## Example
///
/// ```rust
/// use owo_colors::OwoColorize;
///
/// println!("My number is {:#x}!", 10.green());
/// println!("My number is not {}!", 4.on_red());
/// ```
///
/// ## How to decide which method to use
///
/// **Do you have a specific color you want to use?**
///
/// Use the specific color's method, such as [`blue`](OwoColorize::blue) or
/// [`on_green`](OwoColorize::on_green).
///
///
/// **Do you want your colors configurable via generics?**
///
/// Use [`fg`](OwoColorize::fg) and [`bg`](OwoColorize::bg) to make it compile-time configurable.
///
///
/// **Do you need to pick a color at runtime?**
///
/// Use the [`color`](OwoColorize::color), [`on_color`](OwoColorize::on_color),
/// [`truecolor`](OwoColorize::truecolor) or [`on_truecolor`](OwoColorize::on_truecolor).
///
/// **Do you need some other text modifier?**
///
/// * [`bold`](OwoColorize::bold)
/// * [`dimmed`](OwoColorize::dimmed)
/// * [`italic`](OwoColorize::italic)
/// * [`underline`](OwoColorize::underline)
/// * [`blink`](OwoColorize::blink)
/// * [`blink_fast`](OwoColorize::blink_fast)
/// * [`reversed`](OwoColorize::reversed)
/// * [`hidden`](OwoColorize::hidden)
/// * [`strikethrough`](OwoColorize::strikethrough)
///
/// **Do you want it to only display colors if it's a terminal?**
///
/// 1. Enable the `supports-colors` feature
/// 2. Colorize inside [`if_supports_color`](OwoColorize::if_supports_color)
///
/// **Do you need to store a set of colors/effects to apply to multiple things?**
///
/// Use [`style`](OwoColorize::style) to apply a [`Style`].
#[rust_analyzer::completions(ignore_flyimport_methods)]
pub trait OwoColorize: Sized {
// Implementation note: even though the "OwoColorize: Sized" condition _can_ be dropped, we
// currently don't do that for API compatibility reasons.
//
// For example, currently, calling `OwoColorize::fg` on a &str results in a type signature of
// `FgColorDisplay<'_, C, &str>`. Dropping the "OwoColorize: Sized" condition would result in a
// type signature of `FgColorDisplay<'_, C, str>`, which is a visible change.
//
// If we ever do a breaking change to owo-colors in the future, this would be a good candidate.
/// Set the foreground color generically
///
/// ```rust
/// use owo_colors::{OwoColorize, colors::*};
///
/// println!("{}", "red foreground".fg::<Red>());
/// ```
#[must_use]
#[inline(always)]
#[rust_analyzer::completions(ignore_flyimport)]
fn fg<C: Color>(&self) -> FgColorDisplay<'_, C, Self> {
FgColorDisplay(self, PhantomData)
}
/// Set the background color generically.
///
/// ```rust
/// use owo_colors::{OwoColorize, colors::*};
///
/// println!("{}", "black background".bg::<Black>());
/// ```
#[must_use]
#[inline(always)]
#[rust_analyzer::completions(ignore_flyimport)]
fn bg<C: Color>(&self) -> BgColorDisplay<'_, C, Self> {
BgColorDisplay(self, PhantomData)
}
color_methods! {
/// Change the foreground color to black
/// Change the background color to black
Black black on_black,
/// Change the foreground color to red
/// Change the background color to red
Red red on_red,
/// Change the foreground color to green
/// Change the background color to green
Green green on_green,
/// Change the foreground color to yellow
/// Change the background color to yellow
Yellow yellow on_yellow,
/// Change the foreground color to blue
/// Change the background color to blue
Blue blue on_blue,
/// Change the foreground color to magenta
/// Change the background color to magenta
Magenta magenta on_magenta,
/// Change the foreground color to purple
/// Change the background color to purple
Magenta purple on_purple,
/// Change the foreground color to cyan
/// Change the background color to cyan
Cyan cyan on_cyan,
/// Change the foreground color to white
/// Change the background color to white
White white on_white,
/// Change the foreground color to the terminal default
/// Change the background color to the terminal default
Default default_color on_default_color,
/// Change the foreground color to bright black
/// Change the background color to bright black
BrightBlack bright_black on_bright_black,
/// Change the foreground color to bright red
/// Change the background color to bright red
BrightRed bright_red on_bright_red,
/// Change the foreground color to bright green
/// Change the background color to bright green
BrightGreen bright_green on_bright_green,
/// Change the foreground color to bright yellow
/// Change the background color to bright yellow
BrightYellow bright_yellow on_bright_yellow,
/// Change the foreground color to bright blue
/// Change the background color to bright blue
BrightBlue bright_blue on_bright_blue,
/// Change the foreground color to bright magenta
/// Change the background color to bright magenta
BrightMagenta bright_magenta on_bright_magenta,
/// Change the foreground color to bright purple
/// Change the background color to bright purple
BrightMagenta bright_purple on_bright_purple,
/// Change the foreground color to bright cyan
/// Change the background color to bright cyan
BrightCyan bright_cyan on_bright_cyan,
/// Change the foreground color to bright white
/// Change the background color to bright white
BrightWhite bright_white on_bright_white,
}
style_methods! {
/// Make the text bold
bold BoldDisplay,
/// Make the text dim
dimmed DimDisplay,
/// Make the text italicized
italic ItalicDisplay,
/// Make the text underlined
underline UnderlineDisplay,
/// Make the text blink
blink BlinkDisplay,
/// Make the text blink (but fast!)
blink_fast BlinkFastDisplay,
/// Swap the foreground and background colors
reversed ReversedDisplay,
/// Hide the text
hidden HiddenDisplay,
/// Cross out the text
strikethrough StrikeThroughDisplay,
}
/// Set the foreground color at runtime. Only use if you do not know which color will be used at
/// compile-time. If the color is constant, use either [`OwoColorize::fg`] or
/// a color-specific method, such as [`OwoColorize::green`],
///
/// ```rust
/// use owo_colors::{OwoColorize, AnsiColors};
///
/// println!("{}", "green".color(AnsiColors::Green));
/// ```
#[must_use]
#[inline(always)]
#[rust_analyzer::completions(ignore_flyimport)]
fn color<Color: DynColor>(&self, color: Color) -> FgDynColorDisplay<'_, Color, Self> {
FgDynColorDisplay(self, color)
}
/// Set the background color at runtime. Only use if you do not know what color to use at
/// compile-time. If the color is constant, use either [`OwoColorize::bg`] or
/// a color-specific method, such as [`OwoColorize::on_yellow`],
///
/// ```rust
/// use owo_colors::{OwoColorize, AnsiColors};
///
/// println!("{}", "yellow background".on_color(AnsiColors::BrightYellow));
/// ```
#[must_use]
#[inline(always)]
#[rust_analyzer::completions(ignore_flyimport)]
fn on_color<Color: DynColor>(&self, color: Color) -> BgDynColorDisplay<'_, Color, Self> {
BgDynColorDisplay(self, color)
}
/// Set the foreground color to a specific RGB value.
#[must_use]
#[rust_analyzer::completions(ignore_flyimport)]
fn fg_rgb<const R: u8, const G: u8, const B: u8>(
&self,
) -> FgColorDisplay<'_, colors::CustomColor<R, G, B>, Self> {
FgColorDisplay(self, PhantomData)
}
/// Set the background color to a specific RGB value.
#[must_use]
#[rust_analyzer::completions(ignore_flyimport)]
fn bg_rgb<const R: u8, const G: u8, const B: u8>(
&self,
) -> BgColorDisplay<'_, colors::CustomColor<R, G, B>, Self> {
BgColorDisplay(self, PhantomData)
}
/// Sets the foreground color to an RGB value.
#[must_use]
#[inline(always)]
#[rust_analyzer::completions(ignore_flyimport)]
fn truecolor(&self, r: u8, g: u8, b: u8) -> FgDynColorDisplay<'_, Rgb, Self> {
FgDynColorDisplay(self, Rgb(r, g, b))
}
/// Sets the background color to an RGB value.
#[must_use]
#[inline(always)]
#[rust_analyzer::completions(ignore_flyimport)]
fn on_truecolor(&self, r: u8, g: u8, b: u8) -> BgDynColorDisplay<'_, Rgb, Self> {
BgDynColorDisplay(self, Rgb(r, g, b))
}
/// Apply a runtime-determined style
#[must_use]
#[rust_analyzer::completions(ignore_flyimport)]
fn style(&self, style: Style) -> Styled<&Self> {
style.style(self)
}
/// Apply a given transformation function to all formatters if the given stream
/// supports at least basic ANSI colors, allowing you to conditionally apply
/// given styles/colors.
///
/// Requires the `supports-colors` feature.
///
/// ```rust
/// use owo_colors::{Stream, OwoColorize};
///
/// println!(
/// "{}",
/// "woah! error! if this terminal supports colors, it's blue"
/// .if_supports_color(Stream::Stdout, |text| text.bright_blue())
/// );
/// ```
///
/// This function also accepts `supports_color` version 2's `Stream`, and also the deprecated
/// `supports_color` version 1's `Stream`.
///
/// ```rust
/// use owo_colors::OwoColorize;
/// #[cfg(feature = "supports-colors")]
/// use supports_color::Stream;
///
/// println!(
/// "{}",
/// "woah! error! if this terminal supports colors, it's blue"
/// .if_supports_color(Stream::Stdout, |text| text.bright_blue())
/// );
#[must_use]
#[cfg(feature = "supports-colors")]
#[rust_analyzer::completions(ignore_flyimport)]
fn if_supports_color<'a, Out, ApplyFn>(
&'a self,
stream: impl Into<Stream>,
apply: ApplyFn,
) -> SupportsColorsDisplay<'a, Self, Out, ApplyFn>
where
ApplyFn: Fn(&'a Self) -> Out,
{
SupportsColorsDisplay(self, apply, stream.into())
}
}
#[cfg(feature = "supports-colors")]
mod supports_colors;
#[cfg(feature = "supports-colors")]
pub use {
overrides::{set_override, unset_override, with_override},
supports_colors::{Stream, SupportsColorsDisplay},
};
pub use colors::{
ansi_colors::AnsiColors, css::dynamic::CssColors, dynamic::Rgb, xterm::dynamic::XtermColors,
};
// TODO: figure out some wait to only implement for fmt::Display | fmt::Debug | ...
impl<D> OwoColorize for D {}
pub use {
combo::{ComboColorDisplay, ComboDynColorDisplay},
dyn_colors::*,
dyn_styles::*,
};
/// Module for drop-in [`colored`](https://docs.rs/colored) support to aid in porting code from
/// [`colored`](https://docs.rs/colored) to owo-colors.
///
/// Just replace:
///
/// ```rust
/// # mod colored {}
/// use colored::*;
/// ```
///
/// with
///
/// ```rust
/// use owo_colors::colored::*;
/// ```
pub mod colored {
pub use crate::AnsiColors as Color;
pub use crate::OwoColorize;
/// A couple of functions to enable and disable coloring similarly to `colored`
#[cfg(feature = "supports-colors")]
pub mod control {
pub use crate::{set_override, unset_override};
}
}
pub use styled_list::StyledList;
#[cfg(feature = "alloc")]
pub use styled_list::StyledVec;
#[cfg(test)]
mod tests;
+135
View File
@@ -0,0 +1,135 @@
use core::sync::atomic::{AtomicU8, Ordering};
/// Set an override value for whether or not colors are supported using
/// [`set_override`] while executing the closure provided.
///
/// Once the function has executed the value will be reset to the previous set
/// (or unset) override.
///
/// This is especially useful in use-cases where one would like to temporarily
/// override the supported color set, without impacting previous configurations.
///
/// ```
/// # use owo_colors::{Stream, OwoColorize, set_override, unset_override, with_override};
/// # use owo_colors::colors::Black;
/// #
/// set_override(false);
/// assert_eq!("example".if_supports_color(Stream::Stdout, |value| value.bg::<Black>()).to_string(), "example");
///
/// with_override(true, || {
/// assert_eq!("example".if_supports_color(Stream::Stdout, |value| value.bg::<Black>()).to_string(), "\x1b[40mexample\x1b[49m");
/// });
///
/// assert_eq!("example".if_supports_color(Stream::Stdout, |value| value.bg::<Black>()).to_string(), "example");
/// # unset_override() // make sure that other doc tests are not impacted
/// ```
#[cfg(feature = "supports-colors")]
pub fn with_override<T, F: FnOnce() -> T>(enabled: bool, f: F) -> T {
let previous = OVERRIDE.inner();
OVERRIDE.set_force(enabled);
// Use a scope guard to ensure that if `f` panics, the override is still
// caught.
let _guard = ResetOverrideGuard { previous };
f()
}
struct ResetOverrideGuard {
previous: u8,
}
impl Drop for ResetOverrideGuard {
fn drop(&mut self) {
OVERRIDE.set_unchecked(self.previous);
}
}
/// Set an override value for whether or not colors are supported.
///
/// If `true` is passed,
/// [`if_supports_color`](crate::OwoColorize::if_supports_color) will always act
/// as if colors are supported.
///
/// If `false` is passed,
/// [`if_supports_color`](crate::OwoColorize::if_supports_color) will always act
/// as if colors are **not** supported.
///
/// This behavior can be disabled using [`unset_override`], allowing
/// `owo-colors` to return to inferring if colors are supported.
#[cfg(feature = "supports-colors")]
pub fn set_override(enabled: bool) {
OVERRIDE.set_force(enabled);
}
/// Remove any override value for whether or not colors are supported. This
/// means [`if_supports_color`](crate::OwoColorize::if_supports_color) will
/// resume checking if the given terminal output ([`Stream`](crate::Stream))
/// supports colors.
///
/// This override can be set using [`set_override`].
#[cfg(feature = "supports-colors")]
pub fn unset_override() {
OVERRIDE.unset();
}
pub(crate) static OVERRIDE: Override = Override::none();
pub(crate) struct Override(AtomicU8);
const FORCE_MASK: u8 = 0b10;
const FORCE_ENABLE: u8 = 0b11;
const FORCE_DISABLE: u8 = 0b10;
const NO_FORCE: u8 = 0b00;
impl Override {
const fn none() -> Self {
Self(AtomicU8::new(NO_FORCE))
}
fn inner(&self) -> u8 {
self.0.load(Ordering::SeqCst)
}
pub(crate) fn is_force_enabled_or_disabled(&self) -> (bool, bool) {
let inner = self.inner();
(inner == FORCE_ENABLE, inner == FORCE_DISABLE)
}
fn set_force(&self, enable: bool) {
self.set_unchecked(FORCE_MASK | (enable as u8));
}
fn unset(&self) {
self.set_unchecked(0);
}
fn set_unchecked(&self, value: u8) {
self.0.store(value, Ordering::SeqCst);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn with_override_on_panic() {
set_override(false);
std::panic::catch_unwind(|| {
with_override(true, || {
assert_eq!(OVERRIDE.inner(), FORCE_ENABLE);
panic!("test");
});
})
.expect_err("test should panic");
assert_eq!(
OVERRIDE.inner(),
FORCE_DISABLE,
"override should have been reset"
);
}
}
+276
View File
@@ -0,0 +1,276 @@
use crate::{Style, Styled, dyn_styles::StyleFlags};
use core::{
fmt::{self, Display},
marker::PhantomData,
};
#[cfg(feature = "alloc")]
extern crate alloc;
// Hidden trait for use in `StyledList` bounds
mod sealed {
pub trait IsStyled {
type Inner: core::fmt::Display;
fn style(&self) -> &crate::Style;
fn inner(&self) -> &Self::Inner;
}
}
use sealed::IsStyled;
impl<T: IsStyled> IsStyled for &T {
type Inner = T::Inner;
fn style(&self) -> &Style {
<T as IsStyled>::style(*self)
}
fn inner(&self) -> &Self::Inner {
<T as IsStyled>::inner(*self)
}
}
impl<T: Display> IsStyled for Styled<T> {
type Inner = T;
fn style(&self) -> &Style {
&self.style
}
fn inner(&self) -> &T {
&self.target
}
}
/// A collection of [`Styled`] items that are displayed in such a way as to minimize the amount of characters
/// that are written when displayed.
///
/// ```rust
/// use owo_colors::{Style, Styled, StyledList};
///
/// let styled_items = [
/// Style::new().red().style("Hello "),
/// Style::new().green().style("World"),
/// ];
///
/// // 29 characters
/// let normal_length = styled_items.iter().map(|item| format!("{}", item).len()).sum::<usize>();
/// // 25 characters
/// let styled_length = format!("{}", StyledList::from(styled_items)).len();
///
/// assert!(styled_length < normal_length);
/// ```
pub struct StyledList<T, U>(pub T, PhantomData<fn(U)>)
where
T: AsRef<[U]>,
U: IsStyled;
impl<T, U> From<T> for StyledList<T, U>
where
T: AsRef<[U]>,
U: IsStyled,
{
fn from(list: T) -> Self {
Self(list, PhantomData)
}
}
impl<T, U> Display for StyledList<T, U>
where
T: AsRef<[U]>,
U: IsStyled,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// Handle first item manually
let first_item = match self.0.as_ref().first() {
Some(s) => s,
None => return Ok(()),
};
first_item.style().fmt_prefix(f)?;
write!(f, "{}", first_item.inner())?;
// Handle the rest
for window in self.0.as_ref().windows(2) {
let prev = &window[0];
let current = &window[1];
write!(
f,
"{}{}",
current.style().transition_from(prev.style()),
current.inner()
)?;
}
// Print final reset
// SAFETY: We know that the first item exists, thus a last item exists
self.0.as_ref().last().unwrap().style().fmt_suffix(f)
}
}
impl<'a> Style {
/// Returns an enum that indicates how the transition from one style to this style should be printed
fn transition_from(&'a self, from: &Style) -> Transition<'a> {
if self == from {
return Transition::Noop;
}
// Use full reset if transitioning from colored to non-colored
// or if previous style contains properties that are not in this style
if (from.fg.is_some() && self.fg.is_none())
|| (from.bg.is_some() && self.bg.is_none())
|| (from.bold && !self.bold)
|| (!self.style_flags.0 & from.style_flags.0) != 0
{
return Transition::FullReset(self);
}
// Build up a transition style, that does not require a full reset
// Contains all properties from `self` that are not in `from`
let fg = match (self.fg, from.fg) {
(Some(fg), Some(from_fg)) if fg != from_fg => Some(fg),
(Some(fg), None) => Some(fg),
_ => None,
};
let bg = match (self.bg, from.bg) {
(Some(bg), Some(from_bg)) if bg != from_bg => Some(bg),
(Some(bg), None) => Some(bg),
_ => None,
};
let new_style = Style {
fg,
bg,
bold: from.bold ^ self.bold,
style_flags: StyleFlags(self.style_flags.0 ^ from.style_flags.0),
};
Transition::Style(new_style)
}
}
/// How the transition between two styles should be printed
#[cfg_attr(test, derive(Debug, PartialEq))]
enum Transition<'a> {
Noop,
FullReset(&'a Style),
Style(Style),
}
impl fmt::Display for Transition<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
// Styles are equal
Transition::Noop => Ok(()),
// Reset the style & print full prefix
Transition::FullReset(style) => {
write!(f, "\x1B[0m")?;
style.fmt_prefix(f)
}
// Print transition style without resetting the style
Transition::Style(style) => style.fmt_prefix(f),
}
}
}
/// A helper alias for [`StyledList`] for easier usage with [`alloc::vec::Vec`].
#[cfg(feature = "alloc")]
pub type StyledVec<T> = StyledList<alloc::vec::Vec<Styled<T>>, Styled<T>>;
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_styled_list() {
let list = &[
Style::new().red().style("red"),
Style::new().green().italic().style("green italic"),
Style::new().red().bold().style("red bold"),
];
let list = StyledList::from(list);
assert_eq!(
format!("{}", list),
"\x1b[31mred\x1b[32;3mgreen italic\x1b[0m\x1b[31;1mred bold\x1b[0m"
);
}
#[test]
fn test_styled_final_plain() {
let list = &[
Style::new().red().style("red"),
Style::new().green().italic().style("green italic"),
Style::new().style("plain"),
];
let list = StyledList::from(list);
assert_eq!(
format!("{}", list),
"\x1b[31mred\x1b[32;3mgreen italic\x1b[0mplain"
);
}
#[test]
fn test_transition_from_noop() {
let style_current = Style::new().italic().red();
let style_prev = Style::new().italic().red();
assert_eq!(style_current.transition_from(&style_prev), Transition::Noop);
}
#[test]
fn test_transition_from_full_reset() {
let style_current = Style::new().italic().red();
let style_prev = Style::new().italic().dimmed().red();
assert_eq!(
style_current.transition_from(&style_prev),
Transition::FullReset(&style_current)
);
let style_current = Style::new();
let style_prev = Style::new().red();
assert_eq!(
style_current.transition_from(&style_prev),
Transition::FullReset(&style_current)
);
let style_current = Style::new();
let style_prev = Style::new().bold();
assert_eq!(
style_current.transition_from(&style_prev),
Transition::FullReset(&style_current)
);
}
#[test]
fn test_transition_from_style() {
let style_current = Style::new().italic().dimmed().red();
let style_prev = Style::new().italic().red();
assert_eq!(
style_current.transition_from(&style_prev),
Transition::Style(Style::new().dimmed())
);
let style_current = Style::new().red().on_green();
let style_prev = Style::new().red().on_bright_cyan();
assert_eq!(
style_current.transition_from(&style_prev),
Transition::Style(Style::new().on_green())
);
let style_current = Style::new().bold().blue();
let style_prev = Style::new().bold();
assert_eq!(
style_current.transition_from(&style_prev),
Transition::Style(Style::new().blue())
);
}
}
+478
View File
@@ -0,0 +1,478 @@
//! Different display styles (strikethrough, bold, etc.)
use core::fmt;
#[allow(unused_imports)]
use crate::OwoColorize;
use crate::{Style, Styled};
macro_rules! impl_fmt_for_style {
($(($ty:ident, $trait:path, $ansi:literal)),* $(,)?) => {
$(
impl<'a, T: ?Sized + $trait> $trait for $ty<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str($ansi)?;
<_ as $trait>::fmt(&self.0, f)?;
f.write_str("\x1b[0m")
}
}
)*
};
}
/// Transparent wrapper around a type which implements all the formatters the wrapped type does,
/// with the addition of boldening it. Recommended to be constructed using
/// [`OwoColorize`](OwoColorize::bold).
#[repr(transparent)]
pub struct BoldDisplay<'a, T: ?Sized>(pub &'a T);
impl<'a, T: ?Sized> BoldDisplay<'a, T> {
/// Convert self to a generic [`Styled`].
///
/// This method erases color-related type parameters, and can be
/// used to unify types across branches.
///
/// # Example
///
/// ```rust
/// use owo_colors::OwoColorize;
///
/// fn is_bold() -> bool {
/// // ...
/// # true
/// }
///
/// let styled_str = if is_bold() {
/// "hello".bold().into_styled()
/// } else {
/// "hello".dimmed().into_styled()
/// };
///
/// println!("{}", styled_str);
/// # assert_eq!(styled_str.to_string(), "\x1b[1mhello\x1b[0m");
/// ```
pub const fn into_styled(self) -> Styled<&'a T> {
let style = Style::new().bold();
Styled {
style,
target: self.0,
}
}
}
/// Transparent wrapper around a type which implements all the formatters the wrapped type does,
/// with the addition of dimming it. Recommended to be constructed using
/// [`OwoColorize`](OwoColorize::dimmed).
#[repr(transparent)]
pub struct DimDisplay<'a, T: ?Sized>(pub &'a T);
impl<'a, T: ?Sized> DimDisplay<'a, T> {
/// Convert self to a generic [`Styled`].
///
/// This method erases color-related type parameters, and can be
/// used to unify types across branches.
///
/// # Example
///
/// ```rust
/// use owo_colors::OwoColorize;
///
/// fn is_dimmed() -> bool {
/// // ...
/// # true
/// }
///
/// let styled_str = if is_dimmed() {
/// "hello".dimmed().into_styled()
/// } else {
/// "hello".bold().into_styled()
/// };
///
/// println!("{}", styled_str);
/// # assert_eq!(styled_str.to_string(), "\x1b[2mhello\x1b[0m");
/// ```
pub const fn into_styled(self) -> Styled<&'a T> {
let style = Style::new().dimmed();
Styled {
style,
target: self.0,
}
}
}
/// Transparent wrapper around a type which implements all the formatters the wrapped type does,
/// with the addition of italics. Recommended to be constructed using
/// [`OwoColorize`](OwoColorize::italic).
#[repr(transparent)]
pub struct ItalicDisplay<'a, T: ?Sized>(pub &'a T);
impl<'a, T: ?Sized> ItalicDisplay<'a, T> {
/// Convert self to a generic [`Styled`].
///
/// This method erases color-related type parameters, and can be
/// used to unify types across branches.
///
/// # Example
///
/// ```rust
/// use owo_colors::OwoColorize;
///
/// fn is_italic() -> bool {
/// // ...
/// # true
/// }
///
/// let styled_str = if is_italic() {
/// "hello".italic().into_styled()
/// } else {
/// "hello".underline().into_styled()
/// };
///
/// println!("{}", styled_str);
/// # assert_eq!(styled_str.to_string(), "\x1b[3mhello\x1b[0m");
/// ```
pub const fn into_styled(self) -> Styled<&'a T> {
let style = Style::new().italic();
Styled {
style,
target: self.0,
}
}
}
/// Transparent wrapper around a type which implements all the formatters the wrapped type does,
/// while underlining it. Recommended to be constructed using
/// [`OwoColorize`](OwoColorize::underline).
#[repr(transparent)]
pub struct UnderlineDisplay<'a, T: ?Sized>(pub &'a T);
impl<'a, T: ?Sized> UnderlineDisplay<'a, T> {
/// Convert self to a generic [`Styled`].
///
/// This method erases color-related type parameters, and can be
/// used to unify types across branches.
///
/// # Example
///
/// ```rust
/// use owo_colors::OwoColorize;
///
/// fn is_underline() -> bool {
/// // ...
/// # true
/// }
///
/// let styled_str = if is_underline() {
/// "hello".underline().into_styled()
/// } else {
/// "hello".italic().into_styled()
/// };
///
/// println!("{}", styled_str);
/// # assert_eq!(styled_str.to_string(), "\x1b[4mhello\x1b[0m");
/// ```
pub const fn into_styled(self) -> Styled<&'a T> {
let style = Style::new().underline();
Styled {
style,
target: self.0,
}
}
}
/// Transparent wrapper around a type which implements all the formatters the wrapped type does,
/// while blinking. Recommended to be constructed using
/// [`OwoColorize`](OwoColorize::blink).
#[repr(transparent)]
pub struct BlinkDisplay<'a, T: ?Sized>(pub &'a T);
impl<'a, T: ?Sized> BlinkDisplay<'a, T> {
/// Convert self to a generic [`Styled`].
///
/// This method erases color-related type parameters, and can be
/// used to unify types across branches.
///
/// # Example
///
/// ```rust
/// use owo_colors::OwoColorize;
///
/// fn is_blink() -> bool {
/// // ...
/// # true
/// }
///
/// let styled_str = if is_blink() {
/// "hello".blink().into_styled()
/// } else {
/// "hello".hidden().into_styled()
/// };
///
/// println!("{}", styled_str);
/// # assert_eq!(styled_str.to_string(), "\x1b[5mhello\x1b[0m");
/// ```
pub const fn into_styled(self) -> Styled<&'a T> {
let style = Style::new().blink();
Styled {
style,
target: self.0,
}
}
}
/// Transparent wrapper around a type which implements all the formatters the wrapped type does,
/// with the addition of making it blink fast. Use [`OwoColorize`](OwoColorize::blink_fast)
#[repr(transparent)]
pub struct BlinkFastDisplay<'a, T: ?Sized>(pub &'a T);
impl<'a, T: ?Sized> BlinkFastDisplay<'a, T> {
/// Convert self to a generic [`Styled`].
///
/// This method erases color-related type parameters, and can be
/// used to unify types across branches.
///
/// # Example
///
/// ```rust
/// use owo_colors::OwoColorize;
///
/// fn is_blink_fast() -> bool {
/// // ...
/// # true
/// }
///
/// let styled_str = if is_blink_fast() {
/// "hello".blink_fast().into_styled()
/// } else {
/// "hello".reversed().into_styled()
/// };
///
/// println!("{}", styled_str);
/// # assert_eq!(styled_str.to_string(), "\x1b[6mhello\x1b[0m");
/// ```
pub const fn into_styled(self) -> Styled<&'a T> {
let style = Style::new().blink_fast();
Styled {
style,
target: self.0,
}
}
}
/// Transparent wrapper around a type which implements all the formatters the wrapped type does,
/// with the addition of swapping fg and bg colors. Use [`OwoColorize`](OwoColorize::reversed)
#[repr(transparent)]
pub struct ReversedDisplay<'a, T: ?Sized>(pub &'a T);
impl<'a, T: ?Sized> ReversedDisplay<'a, T> {
/// Convert self to a generic [`Styled`].
///
/// This method erases color-related type parameters, and can be
/// used to unify types across branches.
///
/// # Example
///
/// ```rust
/// use owo_colors::OwoColorize;
///
/// fn is_reversed() -> bool {
/// // ...
/// # true
/// }
///
/// let styled_str = if is_reversed() {
/// "hello".reversed().into_styled()
/// } else {
/// "hello".blink_fast().into_styled()
/// };
///
/// println!("{}", styled_str);
/// # assert_eq!(styled_str.to_string(), "\x1b[7mhello\x1b[0m");
/// ```
pub const fn into_styled(self) -> Styled<&'a T> {
let style = Style::new().reversed();
Styled {
style,
target: self.0,
}
}
}
/// Transparent wrapper around a type which implements all the formatters the wrapped type does,
/// with the addition of hiding the text. Use [`OwoColorize`](OwoColorize::hidden).
#[repr(transparent)]
pub struct HiddenDisplay<'a, T: ?Sized>(pub &'a T);
impl<'a, T: ?Sized> HiddenDisplay<'a, T> {
/// Convert self to a generic [`Styled`].
///
/// This method erases color-related type parameters, and can be
/// used to unify types across branches.
///
/// # Example
///
/// ```rust
/// use owo_colors::OwoColorize;
///
/// fn is_hidden() -> bool {
/// // ...
/// # true
/// }
///
/// let styled_str = if is_hidden() {
/// "hello".hidden().into_styled()
/// } else {
/// "hello".blink().into_styled()
/// };
///
/// println!("{}", styled_str);
/// # assert_eq!(styled_str.to_string(), "\x1b[8mhello\x1b[0m");
/// ```
pub const fn into_styled(self) -> Styled<&'a T> {
let style = Style::new().hidden();
Styled {
style,
target: self.0,
}
}
}
/// Transparent wrapper around a type which implements all the formatters the wrapped type does,
/// crossed out. Recommended to be constructed using
/// [`OwoColorize`](OwoColorize::strikethrough).
#[repr(transparent)]
pub struct StrikeThroughDisplay<'a, T: ?Sized>(pub &'a T);
impl<'a, T: ?Sized> StrikeThroughDisplay<'a, T> {
/// Convert self to a generic [`Styled`].
///
/// This method erases color-related type parameters, and can be
/// used to unify types across branches.
///
/// # Example
///
/// ```rust
/// use owo_colors::OwoColorize;
///
/// fn is_strike_through() -> bool {
/// // ...
/// # true
/// }
///
/// let styled_str = if is_strike_through() {
/// "hello".strikethrough().into_styled()
/// } else {
/// "hello".hidden().into_styled()
/// };
///
/// println!("{}", styled_str);
/// # assert_eq!(styled_str.to_string(), "\x1b[9mhello\x1b[0m");
/// ```
pub const fn into_styled(self) -> Styled<&'a T> {
let style = Style::new().strikethrough();
Styled {
style,
target: self.0,
}
}
}
impl_fmt_for_style! {
// Bold
(BoldDisplay, fmt::Display, "\x1b[1m"),
(BoldDisplay, fmt::Debug, "\x1b[1m"),
(BoldDisplay, fmt::UpperHex, "\x1b[1m"),
(BoldDisplay, fmt::LowerHex, "\x1b[1m"),
(BoldDisplay, fmt::Binary, "\x1b[1m"),
(BoldDisplay, fmt::UpperExp, "\x1b[1m"),
(BoldDisplay, fmt::LowerExp, "\x1b[1m"),
(BoldDisplay, fmt::Octal, "\x1b[1m"),
(BoldDisplay, fmt::Pointer, "\x1b[1m"),
// Dim
(DimDisplay, fmt::Display, "\x1b[2m"),
(DimDisplay, fmt::Debug, "\x1b[2m"),
(DimDisplay, fmt::UpperHex, "\x1b[2m"),
(DimDisplay, fmt::LowerHex, "\x1b[2m"),
(DimDisplay, fmt::Binary, "\x1b[2m"),
(DimDisplay, fmt::UpperExp, "\x1b[2m"),
(DimDisplay, fmt::LowerExp, "\x1b[2m"),
(DimDisplay, fmt::Octal, "\x1b[2m"),
(DimDisplay, fmt::Pointer, "\x1b[2m"),
// Italic
(ItalicDisplay, fmt::Display, "\x1b[3m"),
(ItalicDisplay, fmt::Debug, "\x1b[3m"),
(ItalicDisplay, fmt::UpperHex, "\x1b[3m"),
(ItalicDisplay, fmt::LowerHex, "\x1b[3m"),
(ItalicDisplay, fmt::Binary, "\x1b[3m"),
(ItalicDisplay, fmt::UpperExp, "\x1b[3m"),
(ItalicDisplay, fmt::LowerExp, "\x1b[3m"),
(ItalicDisplay, fmt::Octal, "\x1b[3m"),
(ItalicDisplay, fmt::Pointer, "\x1b[3m"),
// Underline
(UnderlineDisplay, fmt::Display, "\x1b[4m"),
(UnderlineDisplay, fmt::Debug, "\x1b[4m"),
(UnderlineDisplay, fmt::UpperHex, "\x1b[4m"),
(UnderlineDisplay, fmt::LowerHex, "\x1b[4m"),
(UnderlineDisplay, fmt::Binary, "\x1b[4m"),
(UnderlineDisplay, fmt::UpperExp, "\x1b[4m"),
(UnderlineDisplay, fmt::LowerExp, "\x1b[4m"),
(UnderlineDisplay, fmt::Octal, "\x1b[4m"),
(UnderlineDisplay, fmt::Pointer, "\x1b[4m"),
// Blink
(BlinkDisplay, fmt::Display, "\x1b[5m"),
(BlinkDisplay, fmt::Debug, "\x1b[5m"),
(BlinkDisplay, fmt::UpperHex, "\x1b[5m"),
(BlinkDisplay, fmt::LowerHex, "\x1b[5m"),
(BlinkDisplay, fmt::Binary, "\x1b[5m"),
(BlinkDisplay, fmt::UpperExp, "\x1b[5m"),
(BlinkDisplay, fmt::LowerExp, "\x1b[5m"),
(BlinkDisplay, fmt::Octal, "\x1b[5m"),
(BlinkDisplay, fmt::Pointer, "\x1b[5m"),
// Blink fast
(BlinkFastDisplay, fmt::Display, "\x1b[6m"),
(BlinkFastDisplay, fmt::Debug, "\x1b[6m"),
(BlinkFastDisplay, fmt::UpperHex, "\x1b[6m"),
(BlinkFastDisplay, fmt::LowerHex, "\x1b[6m"),
(BlinkFastDisplay, fmt::Binary, "\x1b[6m"),
(BlinkFastDisplay, fmt::UpperExp, "\x1b[6m"),
(BlinkFastDisplay, fmt::LowerExp, "\x1b[6m"),
(BlinkFastDisplay, fmt::Octal, "\x1b[6m"),
(BlinkFastDisplay, fmt::Pointer, "\x1b[6m"),
// Reverse video
(ReversedDisplay, fmt::Display, "\x1b[7m"),
(ReversedDisplay, fmt::Debug, "\x1b[7m"),
(ReversedDisplay, fmt::UpperHex, "\x1b[7m"),
(ReversedDisplay, fmt::LowerHex, "\x1b[7m"),
(ReversedDisplay, fmt::Binary, "\x1b[7m"),
(ReversedDisplay, fmt::UpperExp, "\x1b[7m"),
(ReversedDisplay, fmt::LowerExp, "\x1b[7m"),
(ReversedDisplay, fmt::Octal, "\x1b[7m"),
(ReversedDisplay, fmt::Pointer, "\x1b[7m"),
// Hide the text
(HiddenDisplay, fmt::Display, "\x1b[8m"),
(HiddenDisplay, fmt::Debug, "\x1b[8m"),
(HiddenDisplay, fmt::UpperHex, "\x1b[8m"),
(HiddenDisplay, fmt::LowerHex, "\x1b[8m"),
(HiddenDisplay, fmt::Binary, "\x1b[8m"),
(HiddenDisplay, fmt::UpperExp, "\x1b[8m"),
(HiddenDisplay, fmt::LowerExp, "\x1b[8m"),
(HiddenDisplay, fmt::Octal, "\x1b[8m"),
(HiddenDisplay, fmt::Pointer, "\x1b[8m"),
// StrikeThrough
(StrikeThroughDisplay, fmt::Display, "\x1b[9m"),
(StrikeThroughDisplay, fmt::Debug, "\x1b[9m"),
(StrikeThroughDisplay, fmt::UpperHex, "\x1b[9m"),
(StrikeThroughDisplay, fmt::LowerHex, "\x1b[9m"),
(StrikeThroughDisplay, fmt::Binary, "\x1b[9m"),
(StrikeThroughDisplay, fmt::UpperExp, "\x1b[9m"),
(StrikeThroughDisplay, fmt::LowerExp, "\x1b[9m"),
(StrikeThroughDisplay, fmt::Octal, "\x1b[9m"),
(StrikeThroughDisplay, fmt::Pointer, "\x1b[9m"),
}
+106
View File
@@ -0,0 +1,106 @@
use core::fmt;
#[cfg(feature = "supports-colors")]
/// A display wrapper which applies a transformation based on if the given stream supports
/// colored terminal output
pub struct SupportsColorsDisplay<'a, InVal, Out, ApplyFn>(
pub(crate) &'a InVal,
pub(crate) ApplyFn,
pub(crate) Stream,
)
where
InVal: ?Sized,
ApplyFn: Fn(&'a InVal) -> Out;
/// A possible stream source.
#[derive(Clone, Copy, Debug)]
pub enum Stream {
/// Standard output.
Stdout,
/// Standard error.
Stderr,
}
use crate::OVERRIDE;
impl From<supports_color::Stream> for Stream {
fn from(stream: supports_color::Stream) -> Self {
match stream {
supports_color::Stream::Stdout => Self::Stdout,
supports_color::Stream::Stderr => Self::Stderr,
}
}
}
impl From<supports_color_2::Stream> for Stream {
fn from(stream: supports_color_2::Stream) -> Self {
match stream {
supports_color_2::Stream::Stdout => Self::Stdout,
supports_color_2::Stream::Stderr => Self::Stderr,
}
}
}
macro_rules! impl_fmt_for {
($($trait:path),* $(,)?) => {
$(
impl<'a, In, Out, F> $trait for SupportsColorsDisplay<'a, In, Out, F>
where In: $trait,
Out: $trait,
F: Fn(&'a In) -> Out,
{
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let (force_enabled, force_disabled) = OVERRIDE.is_force_enabled_or_disabled();
if force_enabled || (on_cached(self.2) && !force_disabled) {
<Out as $trait>::fmt(&self.1(self.0), f)
} else {
<In as $trait>::fmt(self.0, f)
}
}
}
)*
};
}
fn on_cached(stream: Stream) -> bool {
let stream = match stream {
Stream::Stdout => supports_color::Stream::Stdout,
Stream::Stderr => supports_color::Stream::Stderr,
};
supports_color::on_cached(stream)
.map(|level| level.has_basic)
.unwrap_or(false)
}
impl_fmt_for! {
fmt::Display,
fmt::Debug,
fmt::UpperHex,
fmt::LowerHex,
fmt::Binary,
fmt::UpperExp,
fmt::LowerExp,
fmt::Octal,
fmt::Pointer,
}
#[cfg(test)]
mod test {
use crate::OwoColorize;
#[test]
fn test_supports_color_versions() {
println!(
"{}",
"This might be green"
.if_supports_color(supports_color_2::Stream::Stdout, |x| x.green())
);
println!(
"{}",
"This might be red".if_supports_color(supports_color::Stream::Stdout, |x| x.red())
);
}
}
+93
View File
@@ -0,0 +1,93 @@
use super::OwoColorize;
use super::colors::*;
use crate::Style;
use crate::colors::css::Lavender;
use crate::{AnsiColors, DynColors};
#[test]
fn test_fg() {
assert_eq!("test".fg::<Black>().to_string(), "\x1b[30mtest\x1b[39m");
assert_eq!("blah blah".red().to_string(), "\x1b[31mblah blah\x1b[39m");
}
#[test]
fn test_bg() {
assert_eq!("test".bg::<Black>().to_string(), "\x1b[40mtest\x1b[49m");
assert_eq!(
"blah blah".on_red().to_string(),
"\x1b[41mblah blah\x1b[49m"
);
}
#[test]
fn test_dyn_fg() {
assert_eq!(
"test".color(AnsiColors::Black).to_string(),
"\x1b[30mtest\x1b[39m"
);
assert_eq!(
"blah blah".color(AnsiColors::Red).to_string(),
"\x1b[31mblah blah\x1b[39m"
);
}
#[test]
fn test_dyn_bg() {
assert_eq!(
"test".on_color(AnsiColors::Black).to_string(),
"\x1b[40mtest\x1b[49m"
);
assert_eq!(
"blah blah".on_color(AnsiColors::Red).to_string(),
"\x1b[41mblah blah\x1b[49m"
);
}
#[test]
fn test_bg_effect() {
let style = Style::new().underline().on_blue();
assert_eq!("test".style(style).to_string(), "\x1b[44;4mtest\x1b[0m");
let style = Style::new().italic().on_red();
assert_eq!("test".style(style).to_string(), "\x1b[41;3mtest\x1b[0m");
}
#[test]
fn test_hex() {
assert_eq!(format!("{:08X}", 0xa.red()), "\x1b[31m0000000A\x1b[39m");
}
#[test]
fn test_css_name() {
assert_eq!(
"test".fg::<Lavender>().to_string(),
"\x1b[38;2;230;230;250mtest\x1b[39m"
);
}
#[test]
fn test_parse() {
macro_rules! assert_parse {
($($str:literal == $eq:expr),* $(,)?) => {
$(
assert_eq!($eq, $str.parse().unwrap());
)*
}
}
assert_parse!(
"yellow" == DynColors::Ansi(AnsiColors::Yellow),
"blue" == DynColors::Ansi(AnsiColors::Blue),
"#eb4034" == DynColors::Rgb(235, 64, 52),
);
}
#[test]
fn default_color() {
assert_eq!(
format_args!("red red red {} no color", "default color".default_color())
.red()
.to_string(),
"\x1b[31mred red red \x1b[39mdefault color\x1b[39m no color\x1b[39m"
);
}