#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ThemeColor {
pub rgb: (u8, u8, u8),
pub ansi256: u8,
pub plain_marker: Option<&'static str>,
}
impl ThemeColor {
#[must_use]
pub const fn new(rgb: (u8, u8, u8), ansi256: u8) -> Self {
Self {
rgb,
ansi256,
plain_marker: None,
}
}
#[must_use]
pub const fn with_marker(rgb: (u8, u8, u8), ansi256: u8, marker: &'static str) -> Self {
Self {
rgb,
ansi256,
plain_marker: Some(marker),
}
}
#[must_use]
pub const fn rgb(&self) -> (u8, u8, u8) {
self.rgb
}
#[must_use]
pub const fn ansi256(&self) -> u8 {
self.ansi256
}
#[must_use]
pub const fn plain_marker(&self) -> Option<&'static str> {
self.plain_marker
}
#[must_use]
pub fn color_code(&self) -> String {
let (r, g, b) = self.rgb;
format!("\x1b[38;2;{r};{g};{b}m")
}
}
#[derive(Debug, Clone)]
pub struct Theme {
pub success: ThemeColor,
pub error: ThemeColor,
pub warning: ThemeColor,
pub info: ThemeColor,
pub null_value: ThemeColor,
pub bool_value: ThemeColor,
pub number_value: ThemeColor,
pub string_value: ThemeColor,
pub date_value: ThemeColor,
pub binary_value: ThemeColor,
pub json_value: ThemeColor,
pub uuid_value: ThemeColor,
pub sql_keyword: ThemeColor,
pub sql_string: ThemeColor,
pub sql_number: ThemeColor,
pub sql_comment: ThemeColor,
pub sql_operator: ThemeColor,
pub sql_identifier: ThemeColor,
pub border: ThemeColor,
pub header: ThemeColor,
pub dim: ThemeColor,
pub highlight: ThemeColor,
}
impl Theme {
#[must_use]
pub fn dark() -> Self {
Self {
success: ThemeColor::new((80, 250, 123), 84), error: ThemeColor::new((255, 85, 85), 203), warning: ThemeColor::new((241, 250, 140), 228), info: ThemeColor::new((139, 233, 253), 117),
null_value: ThemeColor::with_marker((98, 114, 164), 60, "NULL"),
bool_value: ThemeColor::new((241, 250, 140), 228), number_value: ThemeColor::new((139, 233, 253), 117), string_value: ThemeColor::new((80, 250, 123), 84), date_value: ThemeColor::new((255, 121, 198), 212), binary_value: ThemeColor::new((255, 184, 108), 215), json_value: ThemeColor::new((189, 147, 249), 141), uuid_value: ThemeColor::new((255, 184, 108), 215),
sql_keyword: ThemeColor::new((255, 121, 198), 212), sql_string: ThemeColor::new((80, 250, 123), 84), sql_number: ThemeColor::new((189, 147, 249), 141), sql_comment: ThemeColor::new((98, 114, 164), 60), sql_operator: ThemeColor::new((255, 85, 85), 203), sql_identifier: ThemeColor::new((248, 248, 242), 255),
border: ThemeColor::new((98, 114, 164), 60), header: ThemeColor::new((248, 248, 242), 255), dim: ThemeColor::new((98, 114, 164), 60), highlight: ThemeColor::new((255, 255, 255), 231), }
}
#[must_use]
pub fn light() -> Self {
Self {
success: ThemeColor::new((40, 167, 69), 34),
error: ThemeColor::new((220, 53, 69), 160),
warning: ThemeColor::new((255, 193, 7), 220),
info: ThemeColor::new((23, 162, 184), 37),
null_value: ThemeColor::with_marker((108, 117, 125), 244, "NULL"),
bool_value: ThemeColor::new((156, 39, 176), 128),
number_value: ThemeColor::new((0, 150, 136), 30),
string_value: ThemeColor::new((76, 175, 80), 34),
date_value: ThemeColor::new((156, 39, 176), 128),
binary_value: ThemeColor::new((255, 152, 0), 208),
json_value: ThemeColor::new((103, 58, 183), 92),
uuid_value: ThemeColor::new((255, 152, 0), 208),
sql_keyword: ThemeColor::new((156, 39, 176), 128),
sql_string: ThemeColor::new((76, 175, 80), 34),
sql_number: ThemeColor::new((103, 58, 183), 92),
sql_comment: ThemeColor::new((108, 117, 125), 244),
sql_operator: ThemeColor::new((220, 53, 69), 160),
sql_identifier: ThemeColor::new((33, 37, 41), 235),
border: ThemeColor::new((108, 117, 125), 244),
header: ThemeColor::new((33, 37, 41), 235),
dim: ThemeColor::new((108, 117, 125), 244),
highlight: ThemeColor::new((0, 0, 0), 16),
}
}
#[must_use]
pub fn new() -> Self {
Self::default()
}
}
impl Default for Theme {
fn default() -> Self {
Self::dark()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_theme_color_new() {
let color = ThemeColor::new((255, 0, 0), 196);
assert_eq!(color.rgb(), (255, 0, 0));
assert_eq!(color.ansi256(), 196);
assert_eq!(color.plain_marker(), None);
}
#[test]
fn test_theme_color_with_marker() {
let color = ThemeColor::with_marker((128, 128, 128), 244, "DIM");
assert_eq!(color.rgb(), (128, 128, 128));
assert_eq!(color.ansi256(), 244);
assert_eq!(color.plain_marker(), Some("DIM"));
}
#[test]
fn test_dark_theme_success_color() {
let theme = Theme::dark();
assert_eq!(theme.success.rgb(), (80, 250, 123));
}
#[test]
fn test_light_theme_error_color() {
let theme = Theme::light();
assert_eq!(theme.error.rgb(), (220, 53, 69));
}
#[test]
fn test_default_is_dark() {
let default = Theme::default();
let dark = Theme::dark();
assert_eq!(default.success.rgb(), dark.success.rgb());
assert_eq!(default.error.rgb(), dark.error.rgb());
}
#[test]
fn test_null_value_has_marker() {
let theme = Theme::dark();
assert_eq!(theme.null_value.plain_marker(), Some("NULL"));
}
#[test]
fn test_theme_clone() {
let theme1 = Theme::dark();
let theme2 = theme1.clone();
assert_eq!(theme1.success.rgb(), theme2.success.rgb());
}
#[test]
fn test_theme_color_copy() {
let color1 = ThemeColor::new((100, 100, 100), 245);
let color2 = color1; assert_eq!(color1.rgb(), color2.rgb());
}
#[test]
fn test_all_dark_theme_colors_have_ansi256() {
let theme = Theme::dark();
let _ = theme.success.ansi256();
let _ = theme.error.ansi256();
let _ = theme.warning.ansi256();
let _ = theme.info.ansi256();
let _ = theme.null_value.ansi256();
let _ = theme.sql_keyword.ansi256();
let _ = theme.border.ansi256();
}
#[test]
fn test_all_light_theme_colors_have_ansi256() {
let theme = Theme::light();
let _ = theme.success.ansi256();
let _ = theme.error.ansi256();
let _ = theme.warning.ansi256();
let _ = theme.info.ansi256();
}
}