1#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
6pub struct Rgb(pub u8, pub u8, pub u8);
7
8impl Rgb {
9 pub const fn from_hex(rgb: u32) -> Self {
10 Self(
11 ((rgb >> 16) & 0xff) as u8,
12 ((rgb >> 8) & 0xff) as u8,
13 (rgb & 0xff) as u8,
14 )
15 }
16
17 pub fn owo(self) -> owo_colors::Rgb {
19 owo_colors::Rgb(self.0, self.1, self.2)
20 }
21
22 pub fn as_hex(self) -> String {
23 format!("#{:02X}{:02X}{:02X}", self.0, self.1, self.2)
24 }
25}
26
27pub struct NordPalette {
29 pub nord0: Rgb,
31 pub nord1: Rgb,
32 pub nord2: Rgb,
33 pub nord3: Rgb,
34 pub nord4: Rgb,
36 pub nord5: Rgb,
37 pub nord6: Rgb,
38 pub nord7: Rgb,
40 pub nord8: Rgb,
41 pub nord9: Rgb,
42 pub nord10: Rgb,
43 pub nord11: Rgb,
45 pub nord12: Rgb,
46 pub nord13: Rgb,
47 pub nord14: Rgb,
48 pub nord15: Rgb,
49}
50
51pub const NORD: NordPalette = NordPalette {
52 nord0: Rgb::from_hex(0x2E3440),
53 nord1: Rgb::from_hex(0x3B4252),
54 nord2: Rgb::from_hex(0x434C5E),
55 nord3: Rgb::from_hex(0x4C566A),
56 nord4: Rgb::from_hex(0xD8DEE9),
57 nord5: Rgb::from_hex(0xE5E9F0),
58 nord6: Rgb::from_hex(0xECEFF4),
59 nord7: Rgb::from_hex(0x8FBCBB),
60 nord8: Rgb::from_hex(0x88C0D0),
61 nord9: Rgb::from_hex(0x81A1C1),
62 nord10: Rgb::from_hex(0x5E81AC),
63 nord11: Rgb::from_hex(0xBF616A),
64 nord12: Rgb::from_hex(0xD08770),
65 nord13: Rgb::from_hex(0xEBCB8B),
66 nord14: Rgb::from_hex(0xA3BE8C),
67 nord15: Rgb::from_hex(0xB48EAD),
68};
69
70#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
73pub enum Role {
74 Primary,
75 Accent,
76 Info,
77 Success,
78 Warn,
79 Error,
80 Dim,
81}
82
83#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
86pub struct RoleMap {
87 pub primary: Rgb,
88 pub accent: Rgb,
89 pub info: Rgb,
90 pub success: Rgb,
91 pub warn: Rgb,
92 pub error: Rgb,
93 pub dim: Rgb,
94}
95
96impl Default for RoleMap {
97 fn default() -> Self {
98 Self {
99 primary: NORD.nord8, accent: NORD.nord15, info: NORD.nord9, success: NORD.nord14, warn: NORD.nord13, error: NORD.nord11, dim: NORD.nord3, }
107 }
108}
109
110impl RoleMap {
111 pub fn color_of(&self, role: Role) -> Rgb {
112 match role {
113 Role::Primary => self.primary,
114 Role::Accent => self.accent,
115 Role::Info => self.info,
116 Role::Success => self.success,
117 Role::Warn => self.warn,
118 Role::Error => self.error,
119 Role::Dim => self.dim,
120 }
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 #[test]
129 fn nord_canonical_hex_values() {
130 assert_eq!(NORD.nord8.as_hex(), "#88C0D0"); assert_eq!(NORD.nord11.as_hex(), "#BF616A"); assert_eq!(NORD.nord14.as_hex(), "#A3BE8C"); assert_eq!(NORD.nord13.as_hex(), "#EBCB8B"); }
135
136 #[test]
137 fn default_rolemap_binds_semantic_to_nord() {
138 let rm = RoleMap::default();
139 assert_eq!(rm.success.as_hex(), "#A3BE8C");
140 assert_eq!(rm.error.as_hex(), "#BF616A");
141 assert_eq!(rm.warn.as_hex(), "#EBCB8B");
142 }
143
144 #[test]
145 fn rgb_owo_roundtrip_preserves_bytes() {
146 let c = NORD.nord8;
147 let o = c.owo();
148 assert_eq!((o.0, o.1, o.2), (c.0, c.1, c.2));
149 }
150}