#![warn(missing_docs)]
mod tests;
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Default)]
pub enum Color {
RGB(u32),
Theme(u8, u8),
#[default]
Default,
Automatic,
Black,
Blue,
Brown,
Cyan,
Gray,
Green,
Lime,
Magenta,
Navy,
Orange,
Pink,
Purple,
Red,
Silver,
White,
Yellow,
}
impl Color {
pub(crate) fn rgb_hex_value(self) -> String {
match self {
Color::Red => "FF0000".to_string(),
Color::Blue => "0000FF".to_string(),
Color::Cyan => "00FFFF".to_string(),
Color::Gray => "808080".to_string(),
Color::Lime => "00FF00".to_string(),
Color::Navy => "000080".to_string(),
Color::Pink => "FFC0CB".to_string(),
Color::Brown => "800000".to_string(),
Color::Green => "008000".to_string(),
Color::White => "FFFFFF".to_string(),
Color::Orange => "FF6600".to_string(),
Color::Purple => "800080".to_string(),
Color::Silver => "C0C0C0".to_string(),
Color::Yellow => "FFFF00".to_string(),
Color::Magenta => "FF00FF".to_string(),
Color::RGB(color) => format!("{color:06X}"),
Color::Theme(_, _) | Color::Default | Color::Automatic | Color::Black => {
"000000".to_string()
}
}
}
pub(crate) fn vml_rgb_hex_value(self) -> String {
match self {
Color::Theme(_, _) | Color::Default | Color::Automatic => "#ffffe1".to_string(),
_ => {
let rgb_color = Self::rgb_hex_value(self).to_lowercase();
format!("#{rgb_color}")
}
}
}
pub(crate) fn argb_hex_value(self) -> String {
format!("FF{}", self.rgb_hex_value())
}
pub(crate) fn attributes(self) -> Vec<(&'static str, String)> {
match self {
Self::Theme(color, shade) => match color {
0 => match shade {
1 => vec![
("theme", color.to_string()),
("tint", "-4.9989318521683403E-2".to_string()),
],
2 => vec![
("theme", color.to_string()),
("tint", "-0.14999847407452621".to_string()),
],
3 => vec![
("theme", color.to_string()),
("tint", "-0.249977111117893".to_string()),
],
4 => vec![
("theme", color.to_string()),
("tint", "-0.34998626667073579".to_string()),
],
5 => vec![
("theme", color.to_string()),
("tint", "-0.499984740745262".to_string()),
],
_ => vec![("theme", color.to_string())],
},
1 => match shade {
1 => vec![
("theme", color.to_string()),
("tint", "0.499984740745262".to_string()),
],
2 => vec![
("theme", color.to_string()),
("tint", "0.34998626667073579".to_string()),
],
3 => vec![
("theme", color.to_string()),
("tint", "0.249977111117893".to_string()),
],
4 => vec![
("theme", color.to_string()),
("tint", "0.14999847407452621".to_string()),
],
5 => vec![
("theme", color.to_string()),
("tint", "4.9989318521683403E-2".to_string()),
],
_ => vec![("theme", color.to_string())],
},
2 => match shade {
1 => vec![
("theme", color.to_string()),
("tint", "-9.9978637043366805E-2".to_string()),
],
2 => vec![
("theme", color.to_string()),
("tint", "-0.249977111117893".to_string()),
],
3 => vec![
("theme", color.to_string()),
("tint", "-0.499984740745262".to_string()),
],
4 => vec![
("theme", color.to_string()),
("tint", "-0.749992370372631".to_string()),
],
5 => vec![
("theme", color.to_string()),
("tint", "-0.89999084444715716".to_string()),
],
_ => vec![("theme", color.to_string())],
},
_ => match shade {
1 => vec![
("theme", color.to_string()),
("tint", "0.79998168889431442".to_string()),
],
2 => vec![
("theme", color.to_string()),
("tint", "0.59999389629810485".to_string()),
],
3 => vec![
("theme", color.to_string()),
("tint", "0.39997558519241921".to_string()),
],
4 => vec![
("theme", color.to_string()),
("tint", "-0.249977111117893".to_string()),
],
5 => vec![
("theme", color.to_string()),
("tint", "-0.499984740745262".to_string()),
],
_ => vec![("theme", color.to_string())],
},
},
_ => vec![("rgb", self.argb_hex_value())],
}
}
pub(crate) fn chart_scheme(self) -> (String, u32, u32) {
match self {
Self::Theme(color, shade) => match color {
0 => match shade {
0 => ("bg1".to_string(), 0, 0),
1 => ("bg1".to_string(), 95000, 0),
2 => ("bg1".to_string(), 85000, 0),
3 => ("bg1".to_string(), 75000, 0),
4 => ("bg1".to_string(), 65000, 0),
5 => ("bg1".to_string(), 50000, 0),
_ => (String::new(), 0, 0),
},
1 => match shade {
0 => ("tx1".to_string(), 0, 0),
1 => ("tx1".to_string(), 50000, 50000),
2 => ("tx1".to_string(), 65000, 35000),
3 => ("tx1".to_string(), 75000, 25000),
4 => ("tx1".to_string(), 85000, 15000),
5 => ("tx1".to_string(), 95000, 5000),
_ => (String::new(), 0, 0),
},
2 => match shade {
0 => ("bg2".to_string(), 0, 0),
1 => ("bg2".to_string(), 90000, 0),
2 => ("bg2".to_string(), 75000, 0),
3 => ("bg2".to_string(), 50000, 0),
4 => ("bg2".to_string(), 25000, 0),
5 => ("bg2".to_string(), 10000, 0),
_ => (String::new(), 0, 0),
},
3 => match shade {
0 => ("tx2".to_string(), 0, 0),
1 => ("tx2".to_string(), 20000, 80000),
2 => ("tx2".to_string(), 40000, 60000),
3 => ("tx2".to_string(), 60000, 40000),
4 => ("tx2".to_string(), 75000, 0),
5 => ("tx2".to_string(), 50000, 0),
_ => (String::new(), 0, 0),
},
4 => match shade {
0 => ("accent1".to_string(), 0, 0),
1 => ("accent1".to_string(), 20000, 80000),
2 => ("accent1".to_string(), 40000, 60000),
3 => ("accent1".to_string(), 60000, 40000),
4 => ("accent1".to_string(), 75000, 0),
5 => ("accent1".to_string(), 50000, 0),
_ => (String::new(), 0, 0),
},
5 => match shade {
0 => ("accent2".to_string(), 0, 0),
1 => ("accent2".to_string(), 20000, 80000),
2 => ("accent2".to_string(), 40000, 60000),
3 => ("accent2".to_string(), 60000, 40000),
4 => ("accent2".to_string(), 75000, 0),
5 => ("accent2".to_string(), 50000, 0),
_ => (String::new(), 0, 0),
},
6 => match shade {
0 => ("accent3".to_string(), 0, 0),
1 => ("accent3".to_string(), 20000, 80000),
2 => ("accent3".to_string(), 40000, 60000),
3 => ("accent3".to_string(), 60000, 40000),
4 => ("accent3".to_string(), 75000, 0),
5 => ("accent3".to_string(), 50000, 0),
_ => (String::new(), 0, 0),
},
7 => match shade {
0 => ("accent4".to_string(), 0, 0),
1 => ("accent4".to_string(), 20000, 80000),
2 => ("accent4".to_string(), 40000, 60000),
3 => ("accent4".to_string(), 60000, 40000),
4 => ("accent4".to_string(), 75000, 0),
5 => ("accent4".to_string(), 50000, 0),
_ => (String::new(), 0, 0),
},
8 => match shade {
0 => ("accent5".to_string(), 0, 0),
1 => ("accent5".to_string(), 20000, 80000),
2 => ("accent5".to_string(), 40000, 60000),
3 => ("accent5".to_string(), 60000, 40000),
4 => ("accent5".to_string(), 75000, 0),
5 => ("accent5".to_string(), 50000, 0),
_ => (String::new(), 0, 0),
},
9 => match shade {
0 => ("accent6".to_string(), 0, 0),
1 => ("accent6".to_string(), 20000, 80000),
2 => ("accent6".to_string(), 40000, 60000),
3 => ("accent6".to_string(), 60000, 40000),
4 => ("accent6".to_string(), 75000, 0),
5 => ("accent6".to_string(), 50000, 0),
_ => (String::new(), 0, 0),
},
_ => (String::new(), 0, 0),
},
_ => (String::new(), 0, 0),
}
}
#[allow(clippy::unreadable_literal)]
pub(crate) fn is_valid(self) -> bool {
match self {
Color::RGB(color) => {
if color > 0xFFFFFF {
eprintln!(
"RGB color '{color:#X}' must be in the the range 0x000000 - 0xFFFFFF."
);
return false;
}
true
}
Color::Theme(color, shade) => {
if color > 9 {
eprintln!("Theme color '{color}' must be in the the range 0 - 9.");
return false;
}
if shade > 5 {
eprintln!("Theme shade '{shade}' must be in the the range 0 - 5.");
return false;
}
true
}
_ => true,
}
}
pub(crate) fn is_auto_or_default(self) -> bool {
self == Color::Automatic || self == Color::Default
}
}
impl From<u32> for Color {
fn from(value: u32) -> Color {
Color::RGB(value)
}
}
impl From<&str> for Color {
fn from(value: &str) -> Color {
let color = if let Some(hex_string) = value.strip_prefix('#') {
u32::from_str_radix(hex_string, 16)
} else {
u32::from_str_radix(value, 16)
};
match color {
Ok(color) => Color::RGB(color),
Err(_) => {
eprintln!("Error parsing '{value}' to RGB color.");
Color::Default
}
}
}
}