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:
17624 files changed
+4564928
-38
No files matched your search
+1
@@ -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
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"git": {
|
||||
"sha1": "baf10f9a74007501379afdc002394a15a787dc59"
|
||||
},
|
||||
"path_in_vcs": ""
|
||||
}
|
||||
Vendored
+74
@@ -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
@@ -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",
|
||||
]
|
||||
Vendored
+74
@@ -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"
|
||||
Vendored
+21
@@ -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.
|
||||
Vendored
+104
@@ -0,0 +1,104 @@
|
||||
# <img src="https://jam1.re/img/rust_owo.svg" height="100"> Colors
|
||||
|
||||
[](https://crates.io/crates/owo-colors)
|
||||
[](https://docs.rs/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.
|
||||
Vendored
+58
@@ -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 })
|
||||
}
|
||||
Vendored
+213
@@ -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;
|
||||
Vendored
+232
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
}
|
||||
Vendored
+945
@@ -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");
|
||||
}
|
||||
}
|
||||
Vendored
+115
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+797
@@ -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));
|
||||
}
|
||||
}
|
||||
Vendored
+557
@@ -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;
|
||||
Vendored
+135
@@ -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"
|
||||
);
|
||||
}
|
||||
}
|
||||
Vendored
+276
@@ -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())
|
||||
);
|
||||
}
|
||||
}
|
||||
Vendored
+478
@@ -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
@@ -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())
|
||||
);
|
||||
}
|
||||
}
|
||||
Vendored
+93
@@ -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"
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user