use ishou_tokens::{ColorPalette, Rgb, SemanticRoles, VellumPalette};
pub use ishou_tokens::FleetTheme;
#[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,
pub text_bright: Rgb,
pub text_muted: Rgb,
pub primary: Rgb,
pub link: Rgb,
pub structural: Rgb,
pub agent: Rgb,
pub search: Rgb,
pub selection: 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); 18] {
[
("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()),
("text_bright", self.text_bright.hex()),
("text_muted", self.text_muted.hex()),
("primary", self.primary.hex()),
("link", self.link.hex()),
("structural", self.structural.hex()),
("agent", self.agent.hex()),
("search", self.search.hex()),
("selection", self.selection.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),
text_bright: r.get(roles.text_bright),
text_muted: r.get(roles.text_muted),
primary: r.get(roles.primary),
link: r.get(roles.link),
structural: r.get(roles.structural),
agent: r.get(roles.agent),
search: r.get(roles.search),
selection: r.get(roles.selection),
}
}
}
#[must_use]
pub fn severity_color(c: &ChromePalette, severity: escriba_shirube::Severity) -> Rgb {
use escriba_shirube::Severity;
match severity {
Severity::Error => c.error,
Severity::Warning => c.warning,
Severity::Info => c.info,
Severity::Hint => c.text_dim,
}
}
#[must_use]
pub fn prescribed_theme_name() -> &'static str {
FleetTheme::prescribed_default().preset_name()
}
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 the_reported_theme_name_is_the_one_actually_painted() {
assert_eq!(
prescribed_theme_name(),
FleetTheme::prescribed_default().preset_name(),
);
let by_name = ChromePalette::for_theme(FleetTheme::prescribed_default());
assert_eq!(by_name.hex_tuple(), ChromePalette::prescribed().hex_tuple());
}
#[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"
);
}
}