//! A simple, lightweight library for working with RGB(A) hexadecimal colors.
//!
//! # Usage
//!
//! This crate is [on crates.io][crates] and can be used by adding `hex_color`
//! to your dependencies in your project's `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! hex_color = "2"
//! ```
//!
//! [crates]: https://crates.io/crates/hex_color
//!
//! # Examples
//!
//! Basic parsing:
//!
//! ```
//! use hex_color::HexColor;
//!
//! # fn main() -> Result<(), hex_color::ParseHexColorError> {
//! let cyan = HexColor::parse("#0FF")?;
//! assert_eq!(cyan, HexColor::CYAN);
//!
//! let transparent_plum = HexColor::parse("#DDA0DD80")?;
//! assert_eq!(transparent_plum, HexColor::rgba(221, 160, 221, 128));
//!
//! // Strictly enforce only an RGB color through parse_rgb:
//! let pink = HexColor::parse_rgb("#FFC0CB")?;
//! assert_eq!(pink, HexColor::rgb(255, 192, 203));
//!
//! // Strictly enforce an alpha component through parse_rgba:
//! let opaque_white = HexColor::parse_rgba("#FFFF")?;
//! assert_eq!(opaque_white, HexColor::WHITE);
//! # Ok(())
//! # }
//! ```
//!
//! Flexible constructors:
//!
//! ```
//! use hex_color::HexColor;
//!
//! let violet = HexColor::rgb(238, 130, 238);
//! let transparent_maroon= HexColor::rgba(128, 0, 0, 128);
//! let transparent_gray = HexColor::GRAY.with_a(128);
//! let lavender = HexColor::from_u24(0x00E6_E6FA);
//! let transparent_lavender = HexColor::from_u32(0xE6E6_FA80);
//! let floral_white = HexColor::WHITE
//! .with_g(250)
//! .with_b(240);
//! ```
//!
//! Comprehensive arithmetic:
//!
//! ```
//! use hex_color::HexColor;
//!
//! assert_eq!(HexColor::BLUE + HexColor::RED, HexColor::MAGENTA);
//! assert_eq!(
//! HexColor::CYAN.saturating_add(HexColor::GRAY),
//! HexColor::rgb(128, 255, 255),
//! );
//! assert_eq!(
//! HexColor::BLACK.wrapping_sub(HexColor::achromatic(1)),
//! HexColor::WHITE,
//! );
//! ```
//!
//! ## With [`rand`](::rand)
//!
//! Using `rand` + `std` features to generate random colors via [`rand`](::rand)
//! out of the box:
//!
//! ```
//! use hex_color::HexColor;
//!
//! let random_rgb: HexColor = rand::random();
//! ```
//!
//! To specify whether an RGB or RGBA color is randomly created, use
//! [`HexColor::random_rgb`] or [`HexColor::random_rgba`] respectively:
//!
//! ```
//! use hex_color::HexColor;
//!
//! let random_rgb = HexColor::random_rgb();
//! let random_rgba = HexColor::random_rgba();
//! ```
//!
//! ## With [`serde`](::serde)
//!
//! Use [`serde`](::serde) to serialize and deserialize colors in multiple
//! formats: [`u24`], [`mod@u32`], [`rgb`], or [`rgba`]:
//!
//! ```
//! use hex_color::HexColor;
//! use serde::{Deserialize, Serialize};
//! use serde_json::json;
//!
//! #[derive(Debug, PartialEq, Deserialize, Serialize)]
//! struct Color {
//! name: String,
//! value: HexColor,
//! }
//!
//! # fn main() -> serde_json::Result<()> {
//! let json_input = json!({
//! "name": "Light Coral",
//! "value": "#F08080",
//! });
//! assert_eq!(
//! serde_json::from_value::<Color>(json_input)?,
//! Color {
//! name: String::from("Light Coral"),
//! value: HexColor::rgb(240, 128, 128),
//! },
//! );
//!
//! let my_color = Color {
//! name: String::from("Dark Salmon"),
//! value: HexColor::rgb(233, 150, 122),
//! };
//! assert_eq!(
//! serde_json::to_value(my_color)?,
//! json!({
//! "name": "Dark Salmon",
//! "value": "#E9967A",
//! }),
//! );
//!
//! #[derive(Debug, PartialEq, Deserialize, Serialize)]
//! struct NumericColor {
//! name: String,
//! #[serde(with = "hex_color::u24")]
//! value: HexColor,
//! }
//!
//! let json_input = json!({
//! "name": "Light Coral",
//! "value": 0x00F0_8080_u32,
//! });
//! assert_eq!(
//! serde_json::from_value::<NumericColor>(json_input)?,
//! NumericColor {
//! name: String::from("Light Coral"),
//! value: HexColor::rgb(240, 128, 128),
//! },
//! );
//!
//! let my_color = NumericColor {
//! name: String::from("Dark Salmon"),
//! value: HexColor::rgb(233, 150, 122),
//! };
//! assert_eq!(
//! serde_json::to_value(my_color)?,
//! json!({
//! "name": "Dark Salmon",
//! "value": 0x00E9_967A_u32,
//! }),
//! );
//! # Ok(())
//! # }
//! ```
//!
//! # Features
//!
//! * `rand` enables out-of-the-box compatability with the [`rand`](::rand)
//! crate.
//! * `serde` enables serialization and deserialization with the
//! [`serde`](::serde) crate.
//! * `std` enables [`std::error::Error`] on [`ParseHexColorError`]. Otherwise,
//! it's needed with `rand` for [`HexColor::random_rgb`],
//! [`HexColor::random_rgba`], and, of course,
//! [`rand::random`](::rand::random).
//!
//! *Note*: Only the `std` feature is enabled by default.
#![cfg_attr(not(feature = "std"), no_std)]
// hex_color types in rustdoc of other crates get linked to here
#![doc(html_root_url = "https://docs.rs/hex_color/2.0.0")]
#![cfg_attr(doc_cfg, feature(doc_cfg))]
#![deny(missing_docs)]
#![deny(clippy::pedantic)]
// This is a necessary evil for "r", "g", "b", "a", and more:
#![allow(clippy::many_single_char_names, clippy::similar_names)]
#[cfg(feature = "rand")]
mod rand;
#[cfg(feature = "serde")]
mod serde;
#[cfg(feature = "serde")]
#[doc(inline)]
pub use self::serde::{rgb, rgba, u24, u32};
use core::{
fmt::{self, Debug, Display},
ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign},
str::{Bytes, FromStr},
};
/// An RGBA color.
///
/// See the [module documentation](self) for details.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct HexColor {
/// The red component of the color.
pub r: u8,
/// The green component of the color.
pub g: u8,
/// The blue component of the color.
pub b: u8,
/// The alpha component of the color (`0` is transparent, `255` is opaque).
pub a: u8,
}
impl HexColor {
////////////////////////////////////////////////////////////////////////////
// Basic colors
////////////////////////////////////////////////////////////////////////////
/// The minimum possible value. RGBA is `(0, 0, 0, 0)`.
pub const MIN: HexColor = HexColor::from_u32(0x0000_0000);
/// The maximum possible value. RGBA is `(255, 255, 255, 255)`.
pub const MAX: HexColor = HexColor::from_u32(0xFFFF_FFFF);
/// Solid black. RGBA is `(0, 0, 0, 255)`.
pub const BLACK: HexColor = HexColor::rgb(0, 0, 0);
/// Solid blue. RGBA is `(0, 0, 255, 255)`.
pub const BLUE: HexColor = HexColor::rgb(0, 0, 255);
/// Completely transparent. RGBA is `(0, 0, 0, 0)`.
pub const CLEAR: HexColor = HexColor::rgba(0, 0, 0, 0);
/// Solid cyan. RGBA is `(0, 255, 255, 255)`.
pub const CYAN: HexColor = HexColor::rgb(0, 255, 255);
/// Solid gray; American spelling of grey. RGBA is `(128, 128, 128, 255)`.
pub const GRAY: HexColor = HexColor::achromatic(128);
/// Solid green. RGBA is `(0, 0, 255, 255)`.
pub const GREEN: HexColor = HexColor::rgb(0, 255, 0);
/// Solid grey; British spelling of gray. RGBA is `(128, 128, 128, 255)`.
pub const GREY: HexColor = HexColor::achromatic(128);
/// Solid magenta. RGBA is `(255, 0, 255, 255)`.
pub const MAGENTA: HexColor = HexColor::rgb(255, 0, 255);
/// Solid red. RGBA is `(255, 0, 0, 255)`.
pub const RED: HexColor = HexColor::rgb(255, 0, 0);
/// Solid white. RGBA is `(255, 255, 255, 255)`.
pub const WHITE: HexColor = HexColor::rgb(255, 255, 255);
/// Solid yellow. RGBA is `(255, 255, 0, 255)`.
pub const YELLOW: HexColor = HexColor::rgb(255, 255, 0);
////////////////////////////////////////////////////////////////////////////
// Constructors
////////////////////////////////////////////////////////////////////////////
/// Constructs a new RGBA value.
///
/// For creating just an RGB value instead, use [`HexColor::rgb`].
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// let red = HexColor::rgba(255, 0, 0, 255);
/// let translucent_red = HexColor::rgba(255, 0, 0, 128);
/// ```
#[must_use]
#[inline]
pub const fn rgba(r: u8, g: u8, b: u8, a: u8) -> HexColor {
HexColor { r, g, b, a }
}
/// Constructs a new RGB value. (The alpha channel defaults to [`u8::MAX`].)
///
/// For creating an RGBA value instead, use [`HexColor::rgba`].
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// let aqua = HexColor::rgb(0, 255, 255);
/// ```
#[must_use]
#[inline]
pub const fn rgb(r: u8, g: u8, b: u8) -> HexColor {
HexColor { r, g, b, a: 255 }
}
/// Constructs a new achromatic RGB value. (The alpha channel defaults to
/// [`u8::MAX`].)
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// assert_eq!(HexColor::achromatic(128), HexColor::rgb(128, 128, 128));
/// ```
///
/// *Note*: There is no "`achromatic_alpha`" constructor or similar method.
/// Instead, it's advised to chain [`HexColor::achromatic`] with
/// [`HexColor::with_a`]:
///
/// ```
/// use hex_color::HexColor;
///
/// let transparent_dark_gray = HexColor::achromatic(64).with_a(128);
/// assert_eq!(transparent_dark_gray, HexColor::rgba(64, 64, 64, 128));
/// ```
#[must_use]
#[inline]
pub const fn achromatic(v: u8) -> HexColor {
HexColor::rgb(v, v, v)
}
////////////////////////////////////////////////////////////////////////////
// Random color generation
////////////////////////////////////////////////////////////////////////////
/// Constructs a new random RGB value through the [`rand`](::rand) crate.
///
/// To generate a random RGBA value, use [`HexColor::random_rgba`] instead.
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// println!("{}", HexColor::random_rgb());
/// ```
#[cfg(all(feature = "rand", feature = "std"))]
#[cfg_attr(doc_cfg, doc(cfg(all(feature = "rand", feature = "std"))))]
#[must_use]
#[inline]
pub fn random_rgb() -> Self {
::rand::random()
}
/// Constructs a new random RGBA value through the [`rand`](::rand) crate.
///
/// To generate a random RGB value, use [`HexColor::random_rgb`] instead.
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// println!("{:#}", HexColor::random_rgba());
/// ```
#[cfg(all(feature = "rand", feature = "std"))]
#[cfg_attr(doc_cfg, doc(cfg(all(feature = "rand", feature = "std"))))]
#[must_use]
#[inline]
pub fn random_rgba() -> Self {
HexColor::random_rgb().with_a(::rand::random())
}
////////////////////////////////////////////////////////////////////////////
// Parsing
////////////////////////////////////////////////////////////////////////////
#[must_use]
unsafe fn parse_shorthand(bytes: &mut Bytes, has_alpha: bool) -> Option<HexColor> {
unsafe fn parse_single_hex_value(bytes: &mut Bytes) -> Option<u8> {
match bytes.next().unwrap_unchecked() {
b'0' => Some(0x00),
b'1' => Some(0x11),
b'2' => Some(0x22),
b'3' => Some(0x33),
b'4' => Some(0x44),
b'5' => Some(0x55),
b'6' => Some(0x66),
b'7' => Some(0x77),
b'8' => Some(0x88),
b'9' => Some(0x99),
b'a' | b'A' => Some(0xAA),
b'b' | b'B' => Some(0xBB),
b'c' | b'C' => Some(0xCC),
b'd' | b'D' => Some(0xDD),
b'e' | b'E' => Some(0xEE),
b'f' | b'F' => Some(0xFF),
_ => None,
}
}
let r = parse_single_hex_value(bytes)?;
let g = parse_single_hex_value(bytes)?;
let b = parse_single_hex_value(bytes)?;
let a = if has_alpha {
parse_single_hex_value(bytes)?
} else {
u8::MAX
};
Some(HexColor::rgba(r, g, b, a))
}
#[must_use]
unsafe fn parse_full(bytes: &mut Bytes, has_alpha: bool) -> Option<HexColor> {
const HEX_RADIX: u32 = 16;
unsafe fn parse_double_hex_value(bytes: &mut Bytes) -> Option<u8> {
let buf = [
bytes.next().unwrap_unchecked(),
bytes.next().unwrap_unchecked(),
];
let s = core::str::from_utf8_unchecked(&buf);
u8::from_str_radix(s, HEX_RADIX).ok()
}
let r = parse_double_hex_value(bytes)?;
let g = parse_double_hex_value(bytes)?;
let b = parse_double_hex_value(bytes)?;
let a = if has_alpha {
parse_double_hex_value(bytes)?
} else {
u8::MAX
};
Some(HexColor::rgba(r, g, b, a))
}
fn parse_internals(s: &str, mode: ParseMode) -> Result<HexColor, ParseHexColorError> {
macro_rules! err {
($variant:ident) => {{
return Err(ParseHexColorError::$variant);
}};
}
let mut bytes = s.bytes();
match bytes.next() {
Some(b'#') => {}
Some(_) => err!(InvalidFormat),
None => err!(Empty),
}
let has_alpha = matches!(s.len(), 5 | 9);
let opt = match (s.len(), mode) {
(4, ParseMode::Rgb | ParseMode::Any) | (5, ParseMode::Rgba | ParseMode::Any) => {
// SAFETY: `bytes` will have either `3` or `4` bytes left and
// `has_alpha` is synchronized.
unsafe { HexColor::parse_shorthand(&mut bytes, has_alpha) }
}
(7, ParseMode::Rgb | ParseMode::Any) | (9, ParseMode::Rgba | ParseMode::Any) => {
// SAFETY: `bytes` will have either `6` or `8` bytes left and
// `has_alpha` is synchronized.
unsafe { HexColor::parse_full(&mut bytes, has_alpha) }
}
_ => err!(InvalidFormat),
};
opt.ok_or(ParseHexColorError::InvalidDigit)
}
/// Parses an RGB(A) hex code.
///
/// **All parsing is case-insensitive**. There are currently four parseable
/// formats:
///
/// * `#RGB`
/// * `#RRGGBB`
/// * `#RGBA`
/// * `#RRGGBBAA`
///
/// To parse *only* a hexadecimal triplet, use [`parse_rgb`]. Otherwise,
/// to parse *only* a hexadecimal quadruplet, use [`parse_rgba`].
///
/// [`parse_rgb`]: HexColor::parse_rgb
/// [`parse_rgba`]: HexColor::parse_rgba
///
/// # Errors
///
/// - [`Empty`] when the input is empty.
/// - [`InvalidFormat`] when the input is a malformed length or lacks a
/// leading `#`. If you suspect there might be whitespace in the input,
/// consider calling [`str::trim`] first.
/// - [`InvalidDigit`] when the format seems correct but one of the
/// characters is an invalid hexadecimal digit.
///
/// [`Empty`]: ParseHexColorError::Empty
/// [`InvalidFormat`]: ParseHexColorError::InvalidFormat
/// [`InvalidDigit`]: ParseHexColorError::InvalidDigit
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// # fn main() -> Result<(), hex_color::ParseHexColorError> {
/// let red = HexColor::parse("#F00")?;
/// assert_eq!(red, HexColor::rgb(0xFF, 0x00, 0x00));
///
/// let plum = HexColor::parse("#DDA0DD")?;
/// assert_eq!(plum, HexColor::rgb(0xDD, 0xA0, 0xDD));
///
/// let opaque_cyan = HexColor::parse("#0FFF")?;
/// assert_eq!(opaque_cyan, HexColor::rgba(0x00, 0xFF, 0xFF, 0xFF));
///
/// let translucent_azure = HexColor::parse("#F0FFFF80")?;
/// assert_eq!(translucent_azure, HexColor::rgba(0xF0, 0xFF, 0xFF, 0x80));
/// # Ok(())
/// # }
/// ```
#[inline]
pub fn parse(s: &str) -> Result<HexColor, ParseHexColorError> {
HexColor::parse_internals(s, ParseMode::Any)
}
/// Parses an RGB hex code.
///
/// **All parsing is case-insensitive**. There are currently two parseable
/// formats:
///
/// * `#RGB`
/// * `#RRGGBB`
///
/// To parse *only* a hexadecimal quadruplet, use [`parse_rgba`]. Otherwise,
/// to parse *both* hexadecimal triplets and quadruplets, use [`parse`].
///
/// [`parse_rgba`]: HexColor::parse_rgba
/// [`parse`]: HexColor::parse
///
/// # Errors
///
/// - [`Empty`] when the input is empty.
/// - [`InvalidFormat`] when the input is a malformed length or lacks a
/// leading `#`. If you suspect there might be whitespace in the input,
/// consider calling [`str::trim`] first.
/// - [`InvalidDigit`] when the format seems correct but one of the
/// characters is an invalid hexadecimal digit.
///
/// *Note*: a valid RGBA input will return an [`InvalidFormat`]. Use
/// [`parse_rgba`] or [`parse`] instead if that behavior is not desired.
///
/// [`Empty`]: ParseHexColorError::Empty
/// [`InvalidFormat`]: ParseHexColorError::InvalidFormat
/// [`InvalidDigit`]: ParseHexColorError::InvalidDigit
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// # fn main() -> Result<(), hex_color::ParseHexColorError> {
/// let yellow = HexColor::parse_rgb("#FF0")?;
/// assert_eq!(yellow, HexColor::rgb(0xFF, 0xFF, 0x00));
///
/// let hot_pink = HexColor::parse_rgb("#FF69B4")?;
/// assert_eq!(hot_pink, HexColor::rgb(0xFF, 0x69, 0xB4));
/// # Ok(())
/// # }
/// ```
#[inline]
pub fn parse_rgb(s: &str) -> Result<HexColor, ParseHexColorError> {
HexColor::parse_internals(s, ParseMode::Rgb)
}
/// Parses an RGBA hex code.
///
/// **All parsing is case-insensitive**. There are currently two parseable
/// formats:
///
/// * `#RGBA`
/// * `#RRGGBBAA`
///
/// To parse *only* a hexadecimal triplet, use [`parse_rgb`]. Otherwise,
/// to parse *both* hexadecimal triplets and quadruplets, use [`parse`].
///
/// [`parse_rgb`]: HexColor::parse_rgb
/// [`parse`]: HexColor::parse
///
/// # Errors
///
/// - [`Empty`] when the input is empty.
/// - [`InvalidFormat`] when the input is a malformed length or lacks a
/// leading `#`. If you suspect there might be whitespace in the input,
/// consider calling [`str::trim`] first.
/// - [`InvalidDigit`] when the format seems correct but one of the
/// characters is an invalid hexadecimal digit.
///
/// **Note**: a valid RGB input (without an alpha value) will return an
/// [`InvalidFormat`]. Use [`parse_rgb`] or [`parse`] instead if that
/// behavior is not desired.
///
/// [`Empty`]: ParseHexColorError::Empty
/// [`InvalidFormat`]: ParseHexColorError::InvalidFormat
/// [`InvalidDigit`]: ParseHexColorError::InvalidDigit
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// # fn main() -> Result<(), hex_color::ParseHexColorError> {
/// let transparent = HexColor::parse_rgba("#FFFF")?;
/// assert_eq!(transparent, HexColor::rgba(0xFF, 0xFF, 0xFF, 0xFF));
///
/// let translucent_gold = HexColor::parse_rgba("#FFD70080")?;
/// assert_eq!(translucent_gold, HexColor::rgba(0xFF, 0xD7, 0x00, 0x80));
/// # Ok(())
/// # }
/// ```
#[inline]
pub fn parse_rgba(s: &str) -> Result<HexColor, ParseHexColorError> {
HexColor::parse_internals(s, ParseMode::Rgba)
}
////////////////////////////////////////////////////////////////////////////
// Other Conversions
////////////////////////////////////////////////////////////////////////////
/// Converts any `u32` in the range `0x0000_0000..=0x00FF_FFFF` to an RGB
/// value.
///
/// To convert any `u32` to an RGBA value, use [`from_u32`] instead.
///
/// [`from_u32`]: HexColor::from_u32
///
/// # Panics
///
/// Panics if `debug_assertions` are enabled and the given value exceeds
/// `0x00FF_FFFF`.
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// let pale_green = HexColor::from_u24(0x98FB98);
/// assert_eq!(pale_green, HexColor::rgb(0x98, 0xFB, 0x98));
/// ```
#[must_use]
#[inline]
#[track_caller]
pub const fn from_u24(n: u32) -> HexColor {
#[cfg(target_endian = "big")]
#[must_use]
const fn inner(v: u32) -> HexColor {
let [_, r, g, b] = v.to_be_bytes();
HexColor::rgb(r, g, b)
}
#[cfg(target_endian = "little")]
#[must_use]
const fn inner(v: u32) -> HexColor {
let [b, g, r, _] = v.to_le_bytes();
HexColor::rgb(r, g, b)
}
debug_assert!(n <= 0x00FF_FFFF);
inner(n)
}
/// Converts any `u32` to an RGBA value.
///
/// For converting a `u32` in the range of `0x0000_0000..=0x00FF_FFFF` to
/// an RGB value, use [`from_u24`] instead.
///
/// [`from_u24`]: HexColor::from_u24
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// let navajo_white = HexColor::from_u32(0xFFDEADFF);
/// assert_eq!(navajo_white, HexColor::rgba(0xFF, 0xDE, 0xAD, 0xFF));
///
/// let translucent_violet = HexColor::from_u32(0xEE82EE80);
/// assert_eq!(translucent_violet, HexColor::rgba(0xEE, 0x82, 0xEE, 0x80));
/// ```
#[must_use]
#[inline]
pub const fn from_u32(v: u32) -> HexColor {
#[cfg(target_endian = "big")]
#[must_use]
const fn inner(v: u32) -> HexColor {
let [r, g, b, a] = v.to_ne_bytes();
HexColor::rgba(r, g, b, a)
}
#[cfg(target_endian = "little")]
#[must_use]
const fn inner(v: u32) -> HexColor {
let [a, b, g, r] = v.to_ne_bytes();
HexColor::rgba(r, g, b, a)
}
inner(v)
}
/// Converts a `HexColor` into a `u32` in the range `0x000000..=0xFFFFFF`,
/// discarding any possibly significant alpha value.
///
/// To convert this `HexColor` into a `u32` containing the alpha value as
/// well, use [`to_u32`].
///
/// [`to_u32`]: HexColor::to_u32
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// let misty_rose = HexColor::rgb(0xFF, 0xE4, 0xE1);
/// assert_eq!(misty_rose.to_u24(), 0xFFE4E1);
///
/// // Again, note that the alpha value is lost in this conversion:
/// let translucent_navy = HexColor::rgba(0x00, 0x00, 0x80, 0x80);
/// assert_eq!(translucent_navy.to_u24(), 0x000080);
/// ```
#[inline]
#[must_use]
pub const fn to_u24(self) -> u32 {
// This isn't needed:
#[cfg(target_endian = "big")]
#[must_use]
const fn inner(color: HexColor) -> u32 {
let (r, g, b, _) = color.split();
u32::from_ne_bytes([0x00, r, g, b])
}
#[cfg(target_endian = "little")]
#[must_use]
const fn inner(color: HexColor) -> u32 {
let (r, g, b, _) = color.split();
u32::from_ne_bytes([b, g, r, 0x00])
}
inner(self)
}
/// Converts a `HexColor` into a `u32`.
///
/// To convert the `HexColor` into a `u32` with only the red, green, and
/// blue channels packed, use [`to_u24`].
///
/// [`to_u24`]: HexColor::to_u24
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// let sea_shell = HexColor::rgb(0xFF, 0xF5, 0xEE);
/// assert_eq!(sea_shell.to_u32(), 0xFFF5EEFF);
///
/// // Unlike `to_u24` the alpha value is preserved
/// let translucent_navy = HexColor::rgba(0x00, 0x00, 0x80, 0x80);
/// assert_eq!(translucent_navy.to_u32(), 0x00008080);
/// ```
#[must_use]
#[inline]
pub const fn to_u32(self) -> u32 {
// This isn't needed:
#[cfg(target_endian = "big")]
#[must_use]
const fn inner(color: HexColor) -> u32 {
let (r, g, b, a) = color.split();
u32::from_ne_bytes([r, g, b, a])
}
#[cfg(target_endian = "little")]
#[must_use]
const fn inner(color: HexColor) -> u32 {
let (r, g, b, a) = color.split();
u32::from_ne_bytes([a, b, g, r])
}
inner(self)
}
/// Converts a `HexColor` into `[r, g, b, a]`.
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// let color = HexColor::from_u32(0x89AB_CDEF);
/// assert_eq!(color.to_be_bytes(), [0x89, 0xAB, 0xCD, 0xEF]);
/// ```
#[must_use]
#[inline]
pub const fn to_be_bytes(self) -> [u8; 4] {
[self.r, self.g, self.b, self.a]
}
/// Converts a `HexColor` into `[a, b, g, r]`.
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// let color = HexColor::from_u32(0x89AB_CDEF);
/// assert_eq!(color.to_le_bytes(), [0xEF, 0xCD, 0xAB, 0x89]);
/// ```
#[must_use]
#[inline]
pub const fn to_le_bytes(self) -> [u8; 4] {
[self.a, self.b, self.g, self.r]
}
////////////////////////////////////////////////////////////////////////////
// Utility methods
////////////////////////////////////////////////////////////////////////////
/// Constructs a new `HexColor` derived from `self` with the red component
/// of `r`.
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// assert_eq!(HexColor::MIN.with_r(255), HexColor::rgba(255, 0, 0, 0));
/// ```
#[must_use]
#[inline]
pub const fn with_r(self, r: u8) -> HexColor {
let (_, g, b, a) = self.split();
HexColor::rgba(r, g, b, a)
}
/// Constructs a new `HexColor` derived from `self` with the green component
/// of `g`.
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// assert_eq!(HexColor::MIN.with_g(255), HexColor::rgba(0, 255, 0, 0));
/// ```
#[must_use]
#[inline]
pub const fn with_g(self, g: u8) -> HexColor {
let (r, _, b, a) = self.split();
HexColor::rgba(r, g, b, a)
}
/// Constructs a new `HexColor` derived from `self` with the blue component
/// of `b`.
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// assert_eq!(HexColor::MIN.with_b(255), HexColor::rgba(0, 0, 255, 0));
/// ```
#[must_use]
#[inline]
pub const fn with_b(self, b: u8) -> HexColor {
let (r, g, _, a) = self.split();
HexColor::rgba(r, g, b, a)
}
/// Constructs a new `HexColor` derived from `self` with the alpha component
/// of `a`.
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// assert_eq!(HexColor::MIN.with_a(255), HexColor::rgba(0, 0, 0, 255));
/// ```
#[must_use]
#[inline]
pub const fn with_a(self, a: u8) -> HexColor {
let (r, g, b, _) = self.split();
HexColor::rgba(r, g, b, a)
}
/// Deconstructs a `HexColor` into a tuple of its components: `(r, g, b,
/// a)`.
///
/// This primarily helps in cleaner deconstruction of `HexColor` instances,
/// especially if the variable bindings aren't the same as the `struct'`s
/// fields.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use hex_color::HexColor;
///
/// let slate_blue = HexColor::from_u24(0x6A5ACD);
/// let (red, green, blue, alpha) = slate_blue.split();
/// ```
///
/// For contrast, here's what it would look like otherwise; it's not
/// terrible, but en masse, it's subjectively annoying:
///
/// ```
/// use hex_color::HexColor;
///
/// let slate_blue = HexColor::from_u24(0x6A5ACD);
/// let HexColor {
/// r: red,
/// g: green,
/// b: blue,
/// a: alpha,
/// } = slate_blue;
/// ```
#[must_use]
#[inline]
pub const fn split(self) -> (u8, u8, u8, u8) {
let HexColor { r, g, b, a } = self;
(r, g, b, a)
}
////////////////////////////////////////////////////////////////////////////
// Arithmetic operations
////////////////////////////////////////////////////////////////////////////
/// Adds two colors together.
///
/// Each component is added separately. The alpha component is ignored
/// entirely, always returning an RGB color (where alpha is the default
/// [`u8::MAX`]).
///
/// # Panics
///
/// Panics if any overflow occurs.
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// assert_eq!(HexColor::BLUE.add(HexColor::GREEN), HexColor::CYAN);
/// assert_eq!(HexColor::RED.add(HexColor::BLUE), HexColor::MAGENTA);
/// assert_eq!(HexColor::GREEN.add(HexColor::RED), HexColor::YELLOW);
/// ```
#[inline]
#[must_use]
#[track_caller]
pub const fn add(self, rhs: HexColor) -> HexColor {
let (r1, g1, b1, _) = self.split();
let (r2, g2, b2, _) = rhs.split();
HexColor::rgb(r1 + r2, g1 + g2, b1 + b2)
}
/// Checked color addition. Computes `self + rhs`, returning [`None`] if
/// overflow occurred.
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// let almost_white = HexColor::achromatic(254);
/// let one = HexColor::achromatic(1);
///
/// assert_eq!(almost_white.checked_add(one), Some(HexColor::WHITE));
/// assert_eq!(HexColor::WHITE.checked_add(one), None);
/// ```
#[inline]
#[must_use]
pub const fn checked_add(self, rhs: HexColor) -> Option<HexColor> {
let (res, flag) = self.overflowing_add(rhs);
// TODO: Use `unlikely!` or some equivalent hint when stable.
if flag {
None
} else {
Some(res)
}
}
/// Calculates `self + rhs`.
///
/// Returns a tuple of the addition along with a boolean indicating whether
/// any arithmetic overflow would occur. If an overflow would have occurred,
/// then the wrapped value is returned.
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// let almost_white = HexColor::achromatic(254);
/// let one = HexColor::achromatic(1);
///
/// assert_eq!(
/// almost_white.overflowing_add(one),
/// (HexColor::WHITE, false),
/// );
/// assert_eq!(
/// HexColor::WHITE.overflowing_add(one),
/// (HexColor::BLACK, true),
/// );
/// ```
#[inline]
#[must_use]
pub const fn overflowing_add(self, rhs: HexColor) -> (HexColor, bool) {
let (r1, g1, b1, _) = self.split();
let (r2, g2, b2, _) = rhs.split();
let (r, r_flag) = r1.overflowing_add(r2);
let (g, g_flag) = g1.overflowing_add(g2);
let (b, b_flag) = b1.overflowing_add(b2);
(HexColor::rgb(r, g, b), r_flag || g_flag || b_flag)
}
/// Saturating color addition. Computes `self + rhs`, saturating at the
/// numeric bounds instead of overflowing.
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// // Even though the green component should exceed 255, it saturates at
/// // 255 instead:
/// assert_eq!(
/// HexColor::YELLOW.saturating_add(HexColor::CYAN),
/// HexColor::WHITE,
/// );
/// ```
#[inline]
#[must_use]
pub const fn saturating_add(self, rhs: HexColor) -> HexColor {
let (r1, g1, b1, _) = self.split();
let (r2, g2, b2, _) = rhs.split();
HexColor::rgb(
r1.saturating_add(r2),
g1.saturating_add(g2),
b1.saturating_add(b2),
)
}
/// Wrapping (modular) addition. Computes `self + rhs`, wrapping around the
/// boundary of [`u8`].
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// let almost_white = HexColor::achromatic(254);
/// let one = HexColor::achromatic(1);
///
/// assert_eq!(almost_white.wrapping_add(one), HexColor::WHITE);
/// assert_eq!(HexColor::WHITE.wrapping_add(one), HexColor::BLACK);
/// ```
#[inline]
#[must_use]
pub const fn wrapping_add(self, rhs: HexColor) -> HexColor {
let (r1, g1, b1, _) = self.split();
let (r2, g2, b2, _) = rhs.split();
HexColor::rgb(
r1.wrapping_add(r2),
g1.wrapping_add(g2),
b1.wrapping_add(b2),
)
}
/// Subtracts one color from another.
///
/// Each component is subtracted separately. The alpha component is ignored
/// entirely, always returning an RGB color (where alpha is the default
/// [`u8::MAX`]).
///
/// # Panics
///
/// Panics if any overflow occurs.
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// assert_eq!(HexColor::MAGENTA.sub(HexColor::BLUE), HexColor::RED);
/// assert_eq!(HexColor::YELLOW.sub(HexColor::RED), HexColor::GREEN);
/// assert_eq!(HexColor::CYAN.sub(HexColor::GREEN), HexColor::BLUE);
/// ```
#[inline]
#[must_use]
#[track_caller]
pub const fn sub(self, rhs: HexColor) -> HexColor {
let (r1, g1, b1, _) = self.split();
let (r2, g2, b2, _) = rhs.split();
HexColor::rgb(r1 - r2, g1 - g2, b1 - b2)
}
/// Subtracts `n` from the `HexColor`'s red, green, and blue components.
///
/// # Panics
///
/// Panics if overflow occurs.
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// assert_eq!(HexColor::WHITE.sub_scalar(255), HexColor::BLACK);
/// ```
#[inline]
#[must_use]
#[track_caller]
pub const fn sub_scalar(self, n: u8) -> HexColor {
self.sub(HexColor::achromatic(n))
}
/// Checked color subtraction. Computes `self - rhs`, returning [`None`] if
/// overflow occurred.
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// let almost_black = HexColor::achromatic(1);
/// let one = HexColor::achromatic(1);
///
/// assert_eq!(almost_black.checked_sub(one), Some(HexColor::BLACK));
/// assert_eq!(HexColor::BLACK.checked_sub(one), None);
/// ```
#[inline]
#[must_use]
pub const fn checked_sub(self, rhs: HexColor) -> Option<HexColor> {
let (res, flag) = self.overflowing_sub(rhs);
// TODO: Use `unlikely!` or some equivalent hint when stable.
if flag {
None
} else {
Some(res)
}
}
/// Calculates `self - rhs`.
///
/// Returns a tuple of the subtraction along with a boolean indicating
/// whether any arithmetic overflow would occur. If an overflow would have
/// occurred, then the wrapped value is returned.
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// let almost_black = HexColor::achromatic(1);
/// let one = HexColor::achromatic(1);
///
/// assert_eq!(
/// almost_black.overflowing_sub(one),
/// (HexColor::BLACK, false),
/// );
/// assert_eq!(
/// HexColor::BLACK.overflowing_sub(one),
/// (HexColor::WHITE, true),
/// );
/// ```
#[inline]
#[must_use]
pub const fn overflowing_sub(self, rhs: HexColor) -> (HexColor, bool) {
let (r1, g1, b1, _) = self.split();
let (r2, g2, b2, _) = rhs.split();
let (r, r_flag) = r1.overflowing_sub(r2);
let (g, g_flag) = g1.overflowing_sub(g2);
let (b, b_flag) = b1.overflowing_sub(b2);
(HexColor::rgb(r, g, b), r_flag || g_flag || b_flag)
}
/// Saturating color subtraction. Computes `self - rhs`, saturating at the
/// numeric bounds instead of overflowing.
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// // Even though the red component should overflow, it saturates at 0
/// // instead:
/// assert_eq!(
/// HexColor::CYAN.saturating_sub(HexColor::YELLOW),
/// HexColor::BLUE,
/// );
/// ```
#[inline]
#[must_use]
pub const fn saturating_sub(self, rhs: HexColor) -> HexColor {
let (r1, g1, b1, _) = self.split();
let (r2, g2, b2, _) = rhs.split();
HexColor::rgb(
r1.saturating_sub(r2),
g1.saturating_sub(g2),
b1.saturating_sub(b2),
)
}
/// Wrapping (modular) subtraction. Computes `self - rhs`, wrapping around
/// the boundary of [`u8`].
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// let almost_black = HexColor::achromatic(1);
/// let one = HexColor::achromatic(1);
///
/// assert_eq!(almost_black.wrapping_sub(one), HexColor::BLACK);
/// assert_eq!(HexColor::BLACK.wrapping_sub(one), HexColor::WHITE);
/// ```
#[inline]
#[must_use]
pub const fn wrapping_sub(self, rhs: HexColor) -> HexColor {
let (r1, g1, b1, _) = self.split();
let (r2, g2, b2, _) = rhs.split();
HexColor::rgb(
r1.wrapping_sub(r2),
g1.wrapping_sub(g2),
b1.wrapping_sub(b2),
)
}
/// Multiplies two colors together.
///
/// Each component is multiplied separately. The alpha component is ignored
/// entirely, always returning an RGB color (where alpha is the default
/// [`u8::MAX`]).
///
/// # Panics
///
/// Panics if any overflow occurs.
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// let a = HexColor::rgb(1, 2, 3);
/// let b = HexColor::rgb(4, 5, 6);
///
/// assert_eq!(a.mul(b), HexColor::rgb(4, 10, 18));
/// ```
#[inline]
#[must_use]
#[track_caller]
pub const fn mul(self, rhs: HexColor) -> HexColor {
let (r1, g1, b1, _) = self.split();
let (r2, g2, b2, _) = rhs.split();
HexColor::rgb(r1 * r2, g1 * g2, b1 * b2)
}
/// Checked color multiplication. Computes `self * rhs`, returning [`None`]
/// if overflow occurred.
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// assert_eq!(
/// HexColor::achromatic(5).checked_mul(HexColor::achromatic(2)),
/// Some(HexColor::achromatic(10)),
/// );
/// assert_eq!(
/// HexColor::MAX.checked_mul(HexColor::achromatic(2)),
/// None,
/// );
/// ```
#[inline]
#[must_use]
pub const fn checked_mul(self, rhs: HexColor) -> Option<HexColor> {
let (res, flag) = self.overflowing_mul(rhs);
// TODO: Use `unlikely!` or some equivalent hint when stable.
if flag {
None
} else {
Some(res)
}
}
/// Calculates `self * rhs`.
///
/// Returns a tuple of the multiplication along with a boolean indicating
/// whether any arithmetic overflow would occur. If an overflow would have
/// occurred, then the wrapped value is returned.
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// assert_eq!(
/// HexColor::achromatic(5).overflowing_mul(HexColor::achromatic(2)),
/// (HexColor::achromatic(10), false),
/// );
/// assert_eq!(
/// HexColor::achromatic(200).overflowing_mul(HexColor::achromatic(2)),
/// (HexColor::achromatic(144), true),
/// );
/// ```
#[inline]
#[must_use]
pub const fn overflowing_mul(self, rhs: HexColor) -> (HexColor, bool) {
let (r1, g1, b1, _) = self.split();
let (r2, g2, b2, _) = rhs.split();
let (r, r_flag) = r1.overflowing_mul(r2);
let (g, g_flag) = g1.overflowing_mul(g2);
let (b, b_flag) = b1.overflowing_mul(b2);
(HexColor::rgb(r, g, b), r_flag || g_flag || b_flag)
}
/// Saturating color multiplication. Computes `self * rhs`, saturating at
/// the numeric bounds instead of overflowing.
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// assert_eq!(
/// HexColor::achromatic(5).saturating_mul(HexColor::achromatic(2)),
/// HexColor::achromatic(10),
/// );
/// assert_eq!(
/// HexColor::achromatic(200).saturating_mul(HexColor::achromatic(2)),
/// HexColor::achromatic(255),
/// );
/// ```
#[inline]
#[must_use]
pub const fn saturating_mul(self, rhs: HexColor) -> HexColor {
let (r1, g1, b1, _) = self.split();
let (r2, g2, b2, _) = rhs.split();
HexColor::rgb(
r1.saturating_mul(r2),
g1.saturating_mul(g2),
b1.saturating_mul(b2),
)
}
/// Wrapping (modular) multiplication. Computes `self * rhs`, wrapping
/// around at the boundary of the type.
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// assert_eq!(
/// HexColor::achromatic(5).wrapping_mul(HexColor::achromatic(2)),
/// HexColor::achromatic(10),
/// );
/// assert_eq!(
/// HexColor::achromatic(200).wrapping_mul(HexColor::achromatic(2)),
/// HexColor::achromatic(144),
/// );
/// ```
#[inline]
#[must_use]
pub const fn wrapping_mul(self, rhs: HexColor) -> HexColor {
let (r1, g1, b1, _) = self.split();
let (r2, g2, b2, _) = rhs.split();
HexColor::rgb(
r1.wrapping_mul(r2),
g1.wrapping_mul(g2),
b1.wrapping_mul(b2),
)
}
/// Divides one color with another.
///
/// Each component is divided separately. The alpha component is ignored
/// entirely, always returning an RGB color (where alpha is the default
/// [`u8::MAX`]).
///
/// # Panics
///
/// Panics if any component is divided by zero.
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// let a = HexColor::rgb(128, 64, 32);
/// let b = HexColor::rgb(2, 4, 8);
///
/// assert_eq!(a.div(b), HexColor::rgb(64, 16, 4));
/// ```
#[inline]
#[must_use]
#[track_caller]
pub const fn div(self, rhs: HexColor) -> HexColor {
let (r1, g1, b1, _) = self.split();
let (r2, g2, b2, _) = rhs.split();
HexColor::rgb(r1 / r2, g1 / g2, b1 / b2)
}
/// Checked color division. Computes `self / rhs`, returning [`None`] if
/// any component of `rhs` is zero.
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// assert_eq!(
/// HexColor::achromatic(128).checked_div(HexColor::achromatic(2)),
/// Some(HexColor::achromatic(64)),
/// );
/// assert_eq!(
/// HexColor::WHITE.checked_div(HexColor::achromatic(0)),
/// None,
/// );
/// ```
#[inline]
#[must_use]
pub const fn checked_div(self, rhs: HexColor) -> Option<HexColor> {
let (r1, g1, b1, _) = self.split();
let (r2, g2, b2, _) = rhs.split();
// TODO: Use `unlikely!` or some equivalent hint when stable.
if r2 == 0 || g2 == 0 || b2 == 0 {
None
} else {
Some(HexColor::rgb(r1 / r2, g1 / g2, b1 / b2))
}
}
////////////////////////////////////////////////////////////////////////////
// "Complex" operations
////////////////////////////////////////////////////////////////////////////
/// Linearly inverts the [`HexColor`].
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// assert_eq!(HexColor::RED.invert(), HexColor::CYAN);
/// ```
#[inline]
#[must_use]
pub const fn invert(self) -> HexColor {
let (r, g, b, a) = self.split();
HexColor::rgba(0xFF - r, 0xFF - g, 0xFF - b, a)
}
}
impl Display for HexColor {
/// Display the `HexColor` as a hexadecimal value.
///
/// # Examples
///
/// ```
/// use hex_color::HexColor;
///
/// let rgba = HexColor::rgba(16, 32, 48, 64);
///
/// // By default it displays only a typical uppercase hexadecimal triplet
/// // even if the alpha is not fully opaque:
/// assert_eq!(format!("{rgba}"), "#102030");
///
/// // To display the alpha component, too, use the alternate flag:
/// assert_eq!(format!("{rgba:#}"), "#10203040");
/// ```
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let (r, g, b, a) = self.split();
write!(f, "#{r:02X}{g:02X}{b:02X}")?;
if f.alternate() {
write!(f, "{a:02X}")?;
}
Ok(())
}
}
////////////////////////////////////////////////////////////////////////////////
// Arithmetic traits
////////////////////////////////////////////////////////////////////////////////
impl Add for HexColor {
type Output = HexColor;
#[inline]
#[track_caller]
fn add(self, rhs: Self) -> Self::Output {
self.add(rhs)
}
}
impl AddAssign for HexColor {
#[inline]
#[track_caller]
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs;
}
}
impl Sub for HexColor {
type Output = HexColor;
#[inline]
#[track_caller]
fn sub(self, rhs: Self) -> Self::Output {
self.sub(rhs)
}
}
impl SubAssign for HexColor {
#[inline]
#[track_caller]
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
impl Mul for HexColor {
type Output = HexColor;
#[inline]
#[track_caller]
fn mul(self, rhs: Self) -> Self::Output {
self.mul(rhs)
}
}
impl MulAssign for HexColor {
#[inline]
#[track_caller]
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
impl Div for HexColor {
type Output = HexColor;
#[inline]
#[track_caller]
fn div(self, rhs: Self) -> Self::Output {
self.div(rhs)
}
}
impl DivAssign for HexColor {
#[inline]
#[track_caller]
fn div_assign(&mut self, rhs: Self) {
*self = *self / rhs;
}
}
////////////////////////////////////////////////////////////////////////////////
// Conversion traits
////////////////////////////////////////////////////////////////////////////////
impl From<u32> for HexColor {
/// Constructs a new `HexColor` from a `u32` via [`HexColor::from_u32`].
#[inline]
fn from(n: u32) -> Self {
HexColor::from_u32(n)
}
}
impl From<HexColor> for u32 {
/// Constructs a new `u32` from a `HexColor` via [`HexColor::to_u32`].
#[inline]
fn from(hex_color: HexColor) -> Self {
hex_color.to_u32()
}
}
impl FromStr for HexColor {
type Err = ParseHexColorError;
/// Semantically identical to [`HexColor::parse`]. For more information,
/// refer to that function's documentation.
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
HexColor::parse_internals(s, ParseMode::Any)
}
}
#[derive(Debug, Copy, Clone)]
enum ParseMode {
Any,
Rgb,
Rgba,
}
/// An error which can be returned when parsing a hex color.
///
/// # Potential causes
///
/// Among other causes, `ParseHexColorError` can be thrown because of leading
/// or trailing whitespace in the string e.g., when it is obtained from user
/// input. Using the [`str::trim()`] method ensures that no whitespace remains
/// before parsing.
#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
#[non_exhaustive]
pub enum ParseHexColorError {
/// The input was empty.
Empty,
/// The string is of a malformed length or does not start with `#`.
InvalidFormat,
/// The format was presumably correct, but one of the digits wasn't
/// hexadecimal.
InvalidDigit,
}
impl Display for ParseHexColorError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let data = match self {
ParseHexColorError::Empty => "cannot parse hex color from empty string",
ParseHexColorError::InvalidFormat => "invalid hexadecimal color format",
ParseHexColorError::InvalidDigit => "invalid hexadecimal digit",
};
f.write_str(data)
}
}
#[cfg(feature = "std")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
impl std::error::Error for ParseHexColorError {}