#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use ratatui::style::Color;
use crate::Theme;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct CustomTheme {
pub name: String,
pub id: String,
pub accent: Color,
#[cfg_attr(feature = "serde", serde(default = "default_dark_gray"))]
pub accent_dim: Color,
#[cfg_attr(feature = "serde", serde(default = "default_white"))]
pub text: Color,
#[cfg_attr(feature = "serde", serde(default = "default_gray"))]
pub text_dim: Color,
#[cfg_attr(feature = "serde", serde(default = "default_white"))]
pub text_bright: Color,
#[cfg_attr(feature = "serde", serde(default = "default_green"))]
pub success: Color,
#[cfg_attr(feature = "serde", serde(default = "default_red"))]
pub error: Color,
#[cfg_attr(feature = "serde", serde(default = "default_yellow"))]
pub warning: Color,
#[cfg_attr(feature = "serde", serde(default = "default_cyan"))]
pub info: Color,
#[cfg_attr(feature = "serde", serde(default = "default_green"))]
pub diff_added: Color,
#[cfg_attr(feature = "serde", serde(default = "default_red"))]
pub diff_removed: Color,
#[cfg_attr(feature = "serde", serde(default = "default_dark_gray"))]
pub diff_context: Color,
#[cfg_attr(feature = "serde", serde(default = "default_dark_gray"))]
pub border: Color,
#[cfg_attr(feature = "serde", serde(default = "default_black"))]
pub surface: Color,
}
impl Theme for CustomTheme {
fn name(&self) -> &str {
&self.name
}
fn id(&self) -> &str {
&self.id
}
fn accent(&self) -> Color {
self.accent
}
fn accent_dim(&self) -> Color {
self.accent_dim
}
fn text(&self) -> Color {
self.text
}
fn text_dim(&self) -> Color {
self.text_dim
}
fn text_bright(&self) -> Color {
self.text_bright
}
fn success(&self) -> Color {
self.success
}
fn error(&self) -> Color {
self.error
}
fn warning(&self) -> Color {
self.warning
}
fn info(&self) -> Color {
self.info
}
fn diff_added(&self) -> Color {
self.diff_added
}
fn diff_removed(&self) -> Color {
self.diff_removed
}
fn diff_context(&self) -> Color {
self.diff_context
}
fn border(&self) -> Color {
self.border
}
fn surface(&self) -> Color {
self.surface
}
}
impl std::fmt::Display for CustomTheme {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} ({})", self.name, self.id)
}
}
#[cfg(feature = "serde")]
fn default_dark_gray() -> Color {
Color::DarkGray
}
#[cfg(feature = "serde")]
fn default_white() -> Color {
Color::White
}
#[cfg(feature = "serde")]
fn default_gray() -> Color {
Color::Gray
}
#[cfg(feature = "serde")]
fn default_green() -> Color {
Color::Green
}
#[cfg(feature = "serde")]
fn default_red() -> Color {
Color::Red
}
#[cfg(feature = "serde")]
fn default_yellow() -> Color {
Color::Yellow
}
#[cfg(feature = "serde")]
fn default_cyan() -> Color {
Color::Cyan
}
#[cfg(feature = "serde")]
fn default_black() -> Color {
Color::Black
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn custom_theme_all_slots_accessible() {
let t = CustomTheme {
name: "Test".to_owned(),
id: "test".to_owned(),
accent: Color::Magenta,
accent_dim: Color::DarkGray,
text: Color::White,
text_dim: Color::Gray,
text_bright: Color::White,
success: Color::Green,
error: Color::Red,
warning: Color::Yellow,
info: Color::Cyan,
diff_added: Color::Green,
diff_removed: Color::Red,
diff_context: Color::DarkGray,
border: Color::DarkGray,
surface: Color::Black,
};
let theme: &dyn Theme = &t;
assert_eq!(theme.accent(), Color::Magenta);
assert_eq!(theme.block_pass(), theme.success());
}
#[test]
fn custom_theme_display() {
let t = CustomTheme {
name: "My Theme".to_owned(),
id: "my-theme".to_owned(),
accent: Color::Red,
accent_dim: Color::DarkGray,
text: Color::White,
text_dim: Color::Gray,
text_bright: Color::White,
success: Color::Green,
error: Color::Red,
warning: Color::Yellow,
info: Color::Cyan,
diff_added: Color::Green,
diff_removed: Color::Red,
diff_context: Color::DarkGray,
border: Color::DarkGray,
surface: Color::Black,
};
assert_eq!(t.to_string(), "My Theme (my-theme)");
}
#[test]
fn custom_theme_equality() {
let a = CustomTheme {
name: "A".to_owned(),
id: "a".to_owned(),
accent: Color::Red,
accent_dim: Color::DarkGray,
text: Color::White,
text_dim: Color::Gray,
text_bright: Color::White,
success: Color::Green,
error: Color::Red,
warning: Color::Yellow,
info: Color::Cyan,
diff_added: Color::Green,
diff_removed: Color::Red,
diff_context: Color::DarkGray,
border: Color::DarkGray,
surface: Color::Black,
};
let b = a.clone();
assert_eq!(a, b);
}
#[cfg(feature = "serde")]
#[test]
fn custom_theme_deserialize_minimal() {
let toml_str = r#"
name = "Minimal"
id = "minimal"
accent = "Magenta"
"#;
let theme: CustomTheme = toml::from_str(toml_str).unwrap();
assert_eq!(theme.name, "Minimal");
assert_eq!(theme.accent, Color::Magenta);
assert_eq!(theme.success, Color::Green); }
#[cfg(feature = "serde")]
#[test]
fn custom_theme_deserialize_rgb() {
let toml_str = r#"
name = "RGB"
id = "rgb"
accent = { Rgb = [249, 115, 22] }
"#;
let theme: CustomTheme = toml::from_str(toml_str).unwrap();
assert_eq!(theme.accent, Color::Rgb(249, 115, 22));
}
#[cfg(feature = "serde")]
#[test]
fn custom_theme_serialize_roundtrip() {
let theme = CustomTheme {
name: "RT".to_owned(),
id: "rt".to_owned(),
accent: Color::Rgb(100, 200, 50),
accent_dim: Color::DarkGray,
text: Color::White,
text_dim: Color::Gray,
text_bright: Color::White,
success: Color::Green,
error: Color::Red,
warning: Color::Yellow,
info: Color::Cyan,
diff_added: Color::Green,
diff_removed: Color::Red,
diff_context: Color::DarkGray,
border: Color::DarkGray,
surface: Color::Black,
};
let toml_str = toml::to_string(&theme).unwrap();
let parsed: CustomTheme = toml::from_str(&toml_str).unwrap();
assert_eq!(theme, parsed);
}
}