#![no_std]
extern crate alloc;
use core::{fmt, marker::PhantomData, ops::Index, str::FromStr};
include!(concat!(env!("OUT_DIR"), "/generated_palette.rs"));
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Palette {
pub latte: Flavor,
pub frappe: Flavor,
pub macchiato: Flavor,
pub mocha: Flavor,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum FlavorName {
Latte,
#[cfg_attr(feature = "serde", serde(rename = "Frappé"))]
Frappe,
Macchiato,
Mocha,
}
pub struct FlavorIterator<'a> {
current: usize,
phantom: PhantomData<&'a ()>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Rgb {
pub r: u8,
pub g: u8,
pub b: u8,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Hex(Rgb);
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Hsl {
pub h: f64,
pub s: f64,
pub l: f64,
}
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Color {
pub name: ColorName,
pub order: u32,
pub accent: bool,
pub hex: Hex,
pub rgb: Rgb,
pub hsl: Hsl,
}
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Flavor {
pub name: FlavorName,
pub emoji: char,
pub order: u32,
pub dark: bool,
pub colors: FlavorColors,
pub ansi_colors: FlavorAnsiColors,
}
pub struct ColorIterator<'a> {
colors: &'a FlavorColors,
current: usize,
}
pub struct AnsiColorIterator<'a> {
ansi_colors: &'a FlavorAnsiColors,
current: usize,
}
pub struct AnsiColorPairsIterator<'a> {
ansi_color_pairs: &'a FlavorAnsiColorPairs,
current: usize,
}
impl Palette {
#[must_use]
pub const fn all_flavors(&self) -> [&Flavor; 4] {
[&self.latte, &self.frappe, &self.macchiato, &self.mocha]
}
#[must_use]
pub const fn iter(&self) -> FlavorIterator<'_> {
FlavorIterator {
current: 0,
phantom: PhantomData,
}
}
}
impl Index<FlavorName> for Palette {
type Output = Flavor;
fn index(&self, index: FlavorName) -> &Self::Output {
match index {
FlavorName::Latte => &self.latte,
FlavorName::Frappe => &self.frappe,
FlavorName::Macchiato => &self.macchiato,
FlavorName::Mocha => &self.mocha,
}
}
}
impl Palette {
#[must_use]
pub const fn get_flavor(&self, name: FlavorName) -> &Flavor {
match name {
FlavorName::Latte => &self.latte,
FlavorName::Frappe => &self.frappe,
FlavorName::Macchiato => &self.macchiato,
FlavorName::Mocha => &self.mocha,
}
}
}
impl fmt::Display for Hex {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Rgb { r, g, b } = self.0;
write!(f, "#{r:02x}{g:02x}{b:02x}")
}
}
#[cfg(feature = "serde")]
mod _hex {
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::{Hex, Rgb};
use alloc::string::String;
use alloc::string::ToString;
impl Serialize for Hex {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl<'de> Deserialize<'de> for Hex {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let hex: String = Deserialize::deserialize(deserializer)?;
let hex: u32 = u32::from_str_radix(hex.trim_start_matches('#'), 16)
.map_err(serde::de::Error::custom)?;
let r = ((hex >> 16) & 0xff) as u8;
let g = ((hex >> 8) & 0xff) as u8;
let b = (hex & 0xff) as u8;
Ok(Self(Rgb { r, g, b }))
}
}
}
impl fmt::Display for FlavorName {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Latte => write!(f, "Latte"),
Self::Frappe => write!(f, "Frappé"),
Self::Macchiato => write!(f, "Macchiato"),
Self::Mocha => write!(f, "Mocha"),
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct ParseFlavorNameError;
impl core::error::Error for ParseFlavorNameError {}
impl core::fmt::Display for ParseFlavorNameError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"invalid flavor identifier, expected one of: latte, frappe, frappé, macchiato, mocha"
)
}
}
impl FromStr for FlavorName {
type Err = ParseFlavorNameError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"latte" => Ok(Self::Latte),
"frappe" | "frappé" => Ok(Self::Frappe),
"macchiato" => Ok(Self::Macchiato),
"mocha" => Ok(Self::Mocha),
_ => Err(ParseFlavorNameError),
}
}
}
impl FlavorName {
#[must_use]
pub const fn identifier(&self) -> &'static str {
match self {
Self::Latte => "latte",
Self::Frappe => "frappe",
Self::Macchiato => "macchiato",
Self::Mocha => "mocha",
}
}
}
impl FlavorColors {
#[must_use]
pub const fn iter(&self) -> ColorIterator<'_> {
ColorIterator {
colors: self,
current: 0,
}
}
}
impl FlavorAnsiColors {
#[must_use]
pub const fn iter(&self) -> AnsiColorIterator<'_> {
AnsiColorIterator {
ansi_colors: self,
current: 0,
}
}
#[must_use]
pub const fn all_pairs(&self) -> FlavorAnsiColorPairs {
self.to_ansi_color_pairs()
}
}
impl FlavorAnsiColorPairs {
#[must_use]
pub const fn iter(&self) -> AnsiColorPairsIterator<'_> {
AnsiColorPairsIterator {
ansi_color_pairs: self,
current: 0,
}
}
}
impl<'a> Iterator for FlavorIterator<'a> {
type Item = &'a Flavor;
fn next(&mut self) -> Option<Self::Item> {
if self.current >= PALETTE.all_flavors().len() {
None
} else {
let flavor = PALETTE.all_flavors()[self.current];
self.current += 1;
Some(flavor)
}
}
}
impl<'a> Iterator for ColorIterator<'a> {
type Item = &'a Color;
fn next(&mut self) -> Option<Self::Item> {
if self.current >= self.colors.all_colors().len() {
None
} else {
let color = self.colors.all_colors()[self.current];
self.current += 1;
Some(color)
}
}
}
impl<'a> Iterator for AnsiColorIterator<'a> {
type Item = &'a AnsiColor;
fn next(&mut self) -> Option<Self::Item> {
if self.current >= self.ansi_colors.all_ansi_colors().len() {
None
} else {
let color = self.ansi_colors.all_ansi_colors()[self.current];
self.current += 1;
Some(color)
}
}
}
impl<'a> Iterator for AnsiColorPairsIterator<'a> {
type Item = &'a AnsiColorPair;
fn next(&mut self) -> Option<Self::Item> {
if self.current >= self.ansi_color_pairs.all_ansi_color_pairs().len() {
None
} else {
let color = self.ansi_color_pairs.all_ansi_color_pairs()[self.current];
self.current += 1;
Some(color)
}
}
}
impl<'a> IntoIterator for &'a Palette {
type Item = &'a Flavor;
type IntoIter = FlavorIterator<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a> IntoIterator for &'a FlavorColors {
type Item = &'a Color;
type IntoIter = ColorIterator<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a> IntoIterator for &'a FlavorAnsiColors {
type Item = &'a AnsiColor;
type IntoIter = AnsiColorIterator<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a> IntoIterator for &'a FlavorAnsiColorPairs {
type Item = &'a AnsiColorPair;
type IntoIter = AnsiColorPairsIterator<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl Flavor {
#[must_use]
pub const fn iter(&self) -> ColorIterator<'_> {
self.colors.iter()
}
#[must_use]
pub const fn identifier(&self) -> &'static str {
self.name.identifier()
}
}
impl<'a> IntoIterator for &'a Flavor {
type Item = &'a Color;
type IntoIter = ColorIterator<'a>;
fn into_iter(self) -> Self::IntoIter {
self.colors.iter()
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct ParseColorNameError;
impl core::error::Error for ParseColorNameError {}
impl core::fmt::Display for ParseColorNameError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "invalid color identifier")
}
}
impl Index<ColorName> for Flavor {
type Output = Color;
fn index(&self, index: ColorName) -> &Self::Output {
self.colors.index(index)
}
}
impl Flavor {
#[must_use]
pub const fn get_color(&self, name: ColorName) -> &Color {
self.colors.get_color(name)
}
}
impl Color {
#[must_use]
pub const fn identifier(&self) -> &'static str {
self.name.identifier()
}
}
impl From<(u8, u8, u8)> for Rgb {
fn from((r, g, b): (u8, u8, u8)) -> Self {
Self { r, g, b }
}
}
impl From<(u8, u8, u8)> for Hex {
fn from((r, g, b): (u8, u8, u8)) -> Self {
Self(Rgb { r, g, b })
}
}
impl From<(f64, f64, f64)> for Hsl {
fn from((h, s, l): (f64, f64, f64)) -> Self {
Self { h, s, l }
}
}
#[cfg(feature = "ansi-term")]
mod ansi_term {
use crate::{AnsiColor, Color};
use alloc::borrow::ToOwned;
impl Color {
pub fn ansi_paint<'a, I, S: 'a + ToOwned + ?Sized>(
&self,
input: I,
) -> ansi_term::ANSIGenericString<'a, S>
where
I: Into<alloc::borrow::Cow<'a, S>>,
<S as ToOwned>::Owned: core::fmt::Debug,
{
ansi_term::Color::RGB(self.rgb.r, self.rgb.g, self.rgb.b).paint(input)
}
}
impl AnsiColor {
pub fn ansi_paint<'a, I, S: 'a + ToOwned + ?Sized>(
&self,
input: I,
) -> ansi_term::ANSIGenericString<'a, S>
where
I: Into<alloc::borrow::Cow<'a, S>>,
<S as ToOwned>::Owned: core::fmt::Debug,
{
ansi_term::Color::RGB(self.rgb.r, self.rgb.g, self.rgb.b).paint(input)
}
}
}
#[cfg(feature = "bevy")]
mod bevy {
use crate::{AnsiColor, Color};
impl From<Color> for bevy::prelude::Color {
fn from(value: Color) -> Self {
#[allow(clippy::cast_possible_truncation)]
Self::hsl(value.hsl.h as f32, value.hsl.s as f32, value.hsl.l as f32)
}
}
impl From<AnsiColor> for bevy::prelude::Color {
fn from(value: AnsiColor) -> Self {
#[allow(clippy::cast_possible_truncation)]
Self::hsl(value.hsl.h as f32, value.hsl.s as f32, value.hsl.l as f32)
}
}
}
#[cfg(feature = "css-colors")]
mod css_colors {
use crate::{AnsiColor, Color};
impl From<Color> for css_colors::RGB {
fn from(value: Color) -> Self {
Self {
r: css_colors::Ratio::from_u8(value.rgb.r),
g: css_colors::Ratio::from_u8(value.rgb.g),
b: css_colors::Ratio::from_u8(value.rgb.b),
}
}
}
impl From<AnsiColor> for css_colors::RGB {
fn from(value: AnsiColor) -> Self {
Self {
r: css_colors::Ratio::from_u8(value.rgb.r),
g: css_colors::Ratio::from_u8(value.rgb.g),
b: css_colors::Ratio::from_u8(value.rgb.b),
}
}
}
impl From<Color> for css_colors::HSL {
fn from(value: Color) -> Self {
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
Self {
h: css_colors::Angle::new(value.hsl.h as u16),
s: css_colors::Ratio::from_f32(value.hsl.s as f32),
l: css_colors::Ratio::from_f32(value.hsl.l as f32),
}
}
}
impl From<AnsiColor> for css_colors::HSL {
fn from(value: AnsiColor) -> Self {
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
Self {
h: css_colors::Angle::new(value.hsl.h as u16),
s: css_colors::Ratio::from_f32(value.hsl.s as f32),
l: css_colors::Ratio::from_f32(value.hsl.l as f32),
}
}
}
}
#[cfg(feature = "iced")]
mod iced {
use crate::{AnsiColor, Color};
impl From<Color> for iced::Color {
fn from(value: Color) -> Self {
Self::from_rgb8(value.rgb.r, value.rgb.g, value.rgb.b)
}
}
impl From<AnsiColor> for iced::Color {
fn from(value: AnsiColor) -> Self {
Self::from_rgb8(value.rgb.r, value.rgb.g, value.rgb.b)
}
}
}
#[cfg(feature = "ratatui")]
mod ratatui {
use crate::{AnsiColor, Color};
impl From<Color> for ratatui_core::style::Color {
fn from(value: Color) -> Self {
Self::Rgb(value.rgb.r, value.rgb.g, value.rgb.b)
}
}
impl From<AnsiColor> for ratatui_core::style::Color {
fn from(value: AnsiColor) -> Self {
Self::Rgb(value.rgb.r, value.rgb.g, value.rgb.b)
}
}
}