use ishou_tokens::{ColorPalette, FleetTheme, Rgb, SemanticRoles, VellumPalette};
#[derive(Debug, Clone, Copy)]
pub struct ChromePalette {
pub background: Rgb,
pub surface: Rgb,
pub text: Rgb,
pub text_dim: Rgb,
pub cursor: Rgb,
pub error: Rgb,
pub warning: Rgb,
pub success: Rgb,
pub info: Rgb,
pub accent: Rgb,
}
impl ChromePalette {
#[must_use]
pub fn for_theme(theme: FleetTheme) -> Self {
match theme {
FleetTheme::PlemeDark | FleetTheme::Bare => {
Self::from_roles(&SemanticRoles::pleme_dark(), &Resolver::Pleme)
}
FleetTheme::Vellum => Self::from_roles(&SemanticRoles::vellum(), &Resolver::Vellum),
FleetTheme::PolarVeil => {
Self::from_roles(&SemanticRoles::vellum(), &Resolver::PolarVeil)
}
}
}
#[must_use]
pub fn prescribed() -> Self {
Self::for_theme(FleetTheme::prescribed_default())
}
#[must_use]
pub fn hex_tuple(&self) -> [(&'static str, String); 10] {
[
("background", self.background.hex()),
("surface", self.surface.hex()),
("text", self.text.hex()),
("text_dim", self.text_dim.hex()),
("cursor", self.cursor.hex()),
("error", self.error.hex()),
("warning", self.warning.hex()),
("success", self.success.hex()),
("info", self.info.hex()),
("accent", self.accent.hex()),
]
}
fn from_roles(roles: &SemanticRoles, r: &Resolver) -> Self {
Self {
background: r.get(roles.background),
surface: r.get(roles.surface),
text: r.get(roles.text),
text_dim: r.get(roles.text_dim),
cursor: r.get(roles.cursor),
error: r.get(roles.error),
warning: r.get(roles.warning),
success: r.get(roles.success),
info: r.get(roles.info),
accent: r.get(roles.accent),
}
}
}
enum Resolver {
Pleme,
Vellum,
PolarVeil,
}
impl Resolver {
fn get(&self, key: &str) -> Rgb {
match self {
Self::Pleme => {
let p = ColorPalette::pleme();
p.get(key)
.unwrap_or_else(|| p.get("snow_storm_2").unwrap_or(Rgb::new(0, 0, 0)))
}
Self::Vellum => {
let p = VellumPalette::vellum();
p.get(key)
.unwrap_or_else(|| p.get("snow1").unwrap_or(Rgb::new(0, 0, 0)))
}
Self::PolarVeil => {
let p = VellumPalette::polar_veil();
p.get(key)
.unwrap_or_else(|| p.get("snow1").unwrap_or(Rgb::new(0, 0, 0)))
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn prescribed_chrome_tracks_the_fleet_theme() {
let prescribed = ChromePalette::prescribed().hex_tuple();
let fleet = ChromePalette::for_theme(FleetTheme::prescribed_default()).hex_tuple();
assert_eq!(
prescribed, fleet,
"prescribed chrome must be the fleet theme's chrome"
);
}
#[test]
fn every_theme_resolves_without_hitting_the_fallback() {
for theme in [
FleetTheme::PlemeDark,
FleetTheme::Vellum,
FleetTheme::PolarVeil,
FleetTheme::Bare,
] {
let black = Rgb::new(0, 0, 0).hex();
for (name, v) in ChromePalette::for_theme(theme).hex_tuple() {
assert_ne!(
v, black,
"{theme:?}: role {name} fell through to the fallback"
);
}
}
}
#[test]
fn distinct_themes_paint_distinctly() {
assert_ne!(
ChromePalette::for_theme(FleetTheme::PlemeDark)
.background
.hex(),
ChromePalette::for_theme(FleetTheme::Vellum)
.background
.hex(),
"Nord and Vellum must not share a ground"
);
}
}