1use ishou_tokens::{Rgb, TokenSet};
20
21use crate::nix_ast::{AttrEntry, NixFile, attrset, str_};
22
23#[must_use]
30pub fn render(t: &TokenSet) -> String {
31 let c = &t.color;
32 let hex = |rgb: Rgb| format!("#{:02X}{:02X}{:02X}", rgb.r, rgb.g, rgb.b);
33
34 let polar = attrset(vec![
35 AttrEntry::new("night0", str_(hex(c.polar_night_0))),
36 AttrEntry::new("night1", str_(hex(c.polar_night_1))),
37 AttrEntry::new("night2", str_(hex(c.polar_night_2))),
38 AttrEntry::new("night3", str_(hex(c.polar_night_3))),
39 ]);
40 let snow = attrset(vec![
41 AttrEntry::new("storm0", str_(hex(c.snow_storm_0))),
42 AttrEntry::new("storm1", str_(hex(c.snow_storm_1))),
43 AttrEntry::new("storm2", str_(hex(c.snow_storm_2))),
44 ]);
45 let frost = attrset(vec![
46 AttrEntry::new("frost0", str_(hex(c.frost_0))),
47 AttrEntry::new("frost1", str_(hex(c.frost_1))),
48 AttrEntry::new("frost2", str_(hex(c.frost_2))),
49 AttrEntry::new("frost3", str_(hex(c.frost_3))),
50 ]);
51 let aurora = attrset(vec![
52 AttrEntry::new("red", str_(hex(c.aurora_red))),
53 AttrEntry::new("orange", str_(hex(c.aurora_orange))),
54 AttrEntry::new("yellow", str_(hex(c.aurora_yellow))),
55 AttrEntry::new("green", str_(hex(c.aurora_green))),
56 AttrEntry::new("purple", str_(hex(c.aurora_purple))),
57 ]);
58
59 let body = attrset(vec![
60 AttrEntry::new("polar", polar),
61 AttrEntry::new("snow", snow).with_blank_above(),
62 AttrEntry::new("frost", frost).with_blank_above(),
63 AttrEntry::new("aurora", aurora).with_blank_above(),
64 ]);
65
66 let file = NixFile::new(
67 [
68 "Generated by ishou-render::nix — DO NOT EDIT",
69 "Source of truth: pleme-io/ishou/crates/ishou-tokens/src/color.rs",
70 "Architecture: pleme-io/theory/THEME-ARCHITECTURE.md",
71 "Shape compatible with the legacy",
72 "`<wrapper>/module/themes/nord/colors.nix` files this output",
73 "replaces — import the package and consume identically.",
74 ],
75 body,
76 );
77
78 file.render()
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84 use ishou_tokens::TokenSet;
85
86 #[test]
87 fn deterministic() {
88 let t = TokenSet::pleme();
89 assert_eq!(render(&t), render(&t));
90 }
91
92 #[test]
93 fn matches_legacy_blackmatter_ghostty_colors_nix() {
94 let out = render(&TokenSet::pleme());
100 let expected_body = "\
101{
102 polar = {
103 night0 = \"#2E3440\";
104 night1 = \"#3B4252\";
105 night2 = \"#434C5E\";
106 night3 = \"#4C566A\";
107 };
108
109 snow = {
110 storm0 = \"#D8DEE9\";
111 storm1 = \"#E5E9F0\";
112 storm2 = \"#ECEFF4\";
113 };
114
115 frost = {
116 frost0 = \"#8FBCBB\";
117 frost1 = \"#88C0D0\";
118 frost2 = \"#81A1C1\";
119 frost3 = \"#5E81AC\";
120 };
121
122 aurora = {
123 red = \"#BF616A\";
124 orange = \"#D08770\";
125 yellow = \"#EBCB8B\";
126 green = \"#A3BE8C\";
127 purple = \"#B48EAD\";
128 };
129}
130";
131 assert!(
132 out.ends_with(expected_body),
133 "rendered output's body must match the legacy colors.nix exactly:\n--- got ---\n{out}--- expected end ---\n{expected_body}"
134 );
135 }
136
137 #[test]
138 fn nord_polar_night_0_lands_at_polar_night0() {
139 let out = render(&TokenSet::pleme());
140 assert!(out.contains("night0 = \"#2E3440\""), "got:\n{out}");
141 }
142
143 #[test]
144 fn header_documents_provenance() {
145 let out = render(&TokenSet::pleme());
146 assert!(out.contains("THEME-ARCHITECTURE.md"));
147 assert!(out.contains("Source of truth"));
148 }
149}