blob: ad5234f4602b4dc419f48d382e35c862e0ce8042 [file] [log] [blame]
use std::fmt;
use std::ops::{Index, IndexMut, Mul};
use std::str::FromStr;
use log::{error, trace};
use serde::de::Visitor;
use serde::{Deserialize, Deserializer, Serialize};
use crate::ansi;
use crate::config::Colors;
pub const COUNT: usize = 269;
pub const RED: Rgb = Rgb { r: 0xff, g: 0x0, b: 0x0 };
pub const YELLOW: Rgb = Rgb { r: 0xff, g: 0xff, b: 0x0 };
#[derive(Debug, Eq, PartialEq, Copy, Clone, Default, Serialize)]
pub struct Rgb {
pub r: u8,
pub g: u8,
pub b: u8,
}
// a multiply function for Rgb, as the default dim is just *2/3
impl Mul<f32> for Rgb {
type Output = Rgb;
fn mul(self, rhs: f32) -> Rgb {
let result = Rgb {
r: (f32::from(self.r) * rhs).max(0.0).min(255.0) as u8,
g: (f32::from(self.g) * rhs).max(0.0).min(255.0) as u8,
b: (f32::from(self.b) * rhs).max(0.0).min(255.0) as u8,
};
trace!("Scaling RGB by {} from {:?} to {:?}", rhs, self, result);
result
}
}
/// Deserialize an Rgb from a hex string
///
/// This is *not* the deserialize impl for Rgb since we want a symmetric
/// serialize/deserialize impl for ref tests.
impl<'de> Deserialize<'de> for Rgb {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct RgbVisitor;
// Used for deserializing reftests
#[derive(Deserialize)]
struct RgbDerivedDeser {
r: u8,
g: u8,
b: u8,
}
impl<'a> Visitor<'a> for RgbVisitor {
type Value = Rgb;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("hex color like 0xff00ff")
}
fn visit_str<E>(self, value: &str) -> ::std::result::Result<Rgb, E>
where
E: ::serde::de::Error,
{
Rgb::from_str(&value[..])
.map_err(|_| E::custom("failed to parse rgb; expected hex color like 0xff00ff"))
}
}
// Return an error if the syntax is incorrect
let value = serde_json::Value::deserialize(deserializer)?;
// Attempt to deserialize from struct form
if let Ok(RgbDerivedDeser { r, g, b }) = RgbDerivedDeser::deserialize(value.clone()) {
return Ok(Rgb { r, g, b });
}
// Deserialize from hex notation (either 0xff00ff or #ff00ff)
match value.deserialize_str(RgbVisitor) {
Ok(rgb) => Ok(rgb),
Err(err) => {
error!("Problem with config: {}; using color #000000", err);
Ok(Rgb::default())
}
}
}
}
impl FromStr for Rgb {
type Err = ();
fn from_str(s: &str) -> ::std::result::Result<Rgb, ()> {
let mut chars = s.chars();
let mut rgb = Rgb::default();
macro_rules! component {
($($c:ident),*) => {
$(
match chars.next().and_then(|c| c.to_digit(16)) {
Some(val) => rgb.$c = (val as u8) << 4,
None => return Err(())
}
match chars.next().and_then(|c| c.to_digit(16)) {
Some(val) => rgb.$c |= val as u8,
None => return Err(())
}
)*
}
}
match chars.next() {
Some('0') => {
if chars.next() != Some('x') {
return Err(());
}
}
Some('#') => (),
_ => return Err(()),
}
component!(r, g, b);
Ok(rgb)
}
}
/// List of indexed colors
///
/// The first 16 entries are the standard ansi named colors. Items 16..232 are
/// the color cube. Items 233..256 are the grayscale ramp. Item 256 is
/// the configured foreground color, item 257 is the configured background
/// color, item 258 is the cursor color. Following that are 8 positions for dim colors.
/// Item 267 is the bright foreground color, 268 the dim foreground.
#[derive(Copy, Clone)]
pub struct List([Rgb; COUNT]);
impl<'a> From<&'a Colors> for List {
fn from(colors: &Colors) -> List {
// Type inference fails without this annotation
let mut list = List([Rgb::default(); COUNT]);
list.fill_named(colors);
list.fill_cube(colors);
list.fill_gray_ramp(colors);
list
}
}
impl List {
pub fn fill_named(&mut self, colors: &Colors) {
// Normals
self[ansi::NamedColor::Black] = colors.normal().black;
self[ansi::NamedColor::Red] = colors.normal().red;
self[ansi::NamedColor::Green] = colors.normal().green;
self[ansi::NamedColor::Yellow] = colors.normal().yellow;
self[ansi::NamedColor::Blue] = colors.normal().blue;
self[ansi::NamedColor::Magenta] = colors.normal().magenta;
self[ansi::NamedColor::Cyan] = colors.normal().cyan;
self[ansi::NamedColor::White] = colors.normal().white;
// Brights
self[ansi::NamedColor::BrightBlack] = colors.bright().black;
self[ansi::NamedColor::BrightRed] = colors.bright().red;
self[ansi::NamedColor::BrightGreen] = colors.bright().green;
self[ansi::NamedColor::BrightYellow] = colors.bright().yellow;
self[ansi::NamedColor::BrightBlue] = colors.bright().blue;
self[ansi::NamedColor::BrightMagenta] = colors.bright().magenta;
self[ansi::NamedColor::BrightCyan] = colors.bright().cyan;
self[ansi::NamedColor::BrightWhite] = colors.bright().white;
self[ansi::NamedColor::BrightForeground] =
colors.primary.bright_foreground.unwrap_or(colors.primary.foreground);
// Foreground and background
self[ansi::NamedColor::Foreground] = colors.primary.foreground;
self[ansi::NamedColor::Background] = colors.primary.background;
// Background for custom cursor colors
self[ansi::NamedColor::Cursor] = colors.cursor.cursor.unwrap_or_else(Rgb::default);
// Dims
self[ansi::NamedColor::DimForeground] =
colors.primary.dim_foreground.unwrap_or(colors.primary.foreground * 0.66);
match colors.dim {
Some(ref dim) => {
trace!("Using config-provided dim colors");
self[ansi::NamedColor::DimBlack] = dim.black;
self[ansi::NamedColor::DimRed] = dim.red;
self[ansi::NamedColor::DimGreen] = dim.green;
self[ansi::NamedColor::DimYellow] = dim.yellow;
self[ansi::NamedColor::DimBlue] = dim.blue;
self[ansi::NamedColor::DimMagenta] = dim.magenta;
self[ansi::NamedColor::DimCyan] = dim.cyan;
self[ansi::NamedColor::DimWhite] = dim.white;
}
None => {
trace!("Deriving dim colors from normal colors");
self[ansi::NamedColor::DimBlack] = colors.normal().black * 0.66;
self[ansi::NamedColor::DimRed] = colors.normal().red * 0.66;
self[ansi::NamedColor::DimGreen] = colors.normal().green * 0.66;
self[ansi::NamedColor::DimYellow] = colors.normal().yellow * 0.66;
self[ansi::NamedColor::DimBlue] = colors.normal().blue * 0.66;
self[ansi::NamedColor::DimMagenta] = colors.normal().magenta * 0.66;
self[ansi::NamedColor::DimCyan] = colors.normal().cyan * 0.66;
self[ansi::NamedColor::DimWhite] = colors.normal().white * 0.66;
}
}
}
pub fn fill_cube(&mut self, colors: &Colors) {
let mut index: usize = 16;
// Build colors
for r in 0..6 {
for g in 0..6 {
for b in 0..6 {
// Override colors 16..232 with the config (if present)
if let Some(indexed_color) =
colors.indexed_colors.iter().find(|ic| ic.index == index as u8)
{
self[index] = indexed_color.color;
} else {
self[index] = Rgb {
r: if r == 0 { 0 } else { r * 40 + 55 },
b: if b == 0 { 0 } else { b * 40 + 55 },
g: if g == 0 { 0 } else { g * 40 + 55 },
};
}
index += 1;
}
}
}
debug_assert!(index == 232);
}
pub fn fill_gray_ramp(&mut self, colors: &Colors) {
let mut index: usize = 232;
for i in 0..24 {
// Index of the color is number of named colors + number of cube colors + i
let color_index = 16 + 216 + i;
// Override colors 232..256 with the config (if present)
if let Some(indexed_color) =
colors.indexed_colors.iter().find(|ic| ic.index == color_index)
{
self[index] = indexed_color.color;
index += 1;
continue;
}
let value = i * 10 + 8;
self[index] = Rgb { r: value, g: value, b: value };
index += 1;
}
debug_assert!(index == 256);
}
}
impl fmt::Debug for List {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("List[..]")
}
}
impl Index<ansi::NamedColor> for List {
type Output = Rgb;
#[inline]
fn index(&self, idx: ansi::NamedColor) -> &Self::Output {
&self.0[idx as usize]
}
}
impl IndexMut<ansi::NamedColor> for List {
#[inline]
fn index_mut(&mut self, idx: ansi::NamedColor) -> &mut Self::Output {
&mut self.0[idx as usize]
}
}
impl Index<usize> for List {
type Output = Rgb;
#[inline]
fn index(&self, idx: usize) -> &Self::Output {
&self.0[idx]
}
}
impl IndexMut<usize> for List {
#[inline]
fn index_mut(&mut self, idx: usize) -> &mut Self::Output {
&mut self.0[idx]
}
}
impl Index<u8> for List {
type Output = Rgb;
#[inline]
fn index(&self, idx: u8) -> &Self::Output {
&self.0[idx as usize]
}
}
impl IndexMut<u8> for List {
#[inline]
fn index_mut(&mut self, idx: u8) -> &mut Self::Output {
&mut self.0[idx as usize]
}
}