ishou_render/
fleet_fonts.rs1use ishou_tokens::TokenSet;
63use ishou_tokens::typography::{ItalicStyle, MonoFonts};
64
65use crate::nix_ast::{AttrEntry, NixExpr, NixFile, attrset, lambda, list, raw, str_};
66
67#[must_use]
69pub fn render(t: &TokenSet) -> String {
70 let mono = &t.typography.mono_fonts;
71
72 let primary = attrset(vec![
73 AttrEntry::new("name", str_(mono.primary)),
74 AttrEntry::new("package", primary_pkg(mono)),
75 ]);
76
77 let italic = attrset(vec![
78 AttrEntry::new("name", str_(mono.italic)),
79 AttrEntry::new("style_intent", str_(italic_style_name(mono.italic_style))),
80 AttrEntry::new("package", italic_pkg(mono)),
81 ]);
82
83 let bold = attrset(vec![
84 AttrEntry::new("name", str_(mono.bold)),
85 AttrEntry::new("package", primary_pkg(mono)),
86 ]);
87
88 let symbols = attrset(vec![
89 AttrEntry::new("name", str_("Symbols Nerd Font Mono")),
90 AttrEntry::new("package", raw("pkgs.nerd-fonts.symbols-only")),
91 ]);
92
93 let emoji = attrset(vec![
94 AttrEntry::new("name", str_("Apple Color Emoji")),
95 AttrEntry::new("fallback_name", str_("Noto Color Emoji")),
96 AttrEntry::new("package", NixExpr::Null),
97 ]);
98
99 let fallback_chain = list(mono.fallback.iter().map(|name| str_(*name)).collect());
100
101 let body = attrset(vec![
102 AttrEntry::new("primary", primary).with_comment([
103 "Primary monospace family.",
104 "Source of every cell's regular glyph.",
105 ]),
106 AttrEntry::new("italic", italic)
107 .with_comment(["Italic face — same family slanted (ghostty's model)."]),
108 AttrEntry::new("bold", bold).with_comment(["Bold face — shares the primary package."]),
109 AttrEntry::new("symbols", symbols)
110 .with_comment(["Nerd Font icons (powerline / starship / atuin)."]),
111 AttrEntry::new("emoji", emoji).with_comment([
112 "macOS uses Apple Color Emoji at the OS layer;",
113 "Linux substitutes Noto Color Emoji (operator-installed).",
114 ]),
115 AttrEntry::new("fallback_chain", fallback_chain)
116 .with_comment(["Ordered fallback list cosmic-text walks for missing codepoints."]),
117 ]);
118
119 let file = NixFile::new(
120 [
121 "Generated by ishou-render::fleet_fonts — DO NOT EDIT",
122 "Source of truth: pleme-io/ishou/crates/ishou-tokens/src/typography.rs",
123 "Architecture: pleme-io/theory/THEME-ARCHITECTURE.md",
124 "",
125 "Consumed as `let fonts = import this { inherit pkgs; }; in …`",
126 "by every pleme-io GPU app's HM module (mado, ghostty, fumi, …).",
127 ],
128 lambda(vec!["pkgs"], body),
129 );
130
131 file.render()
132}
133
134fn primary_pkg(mono: &MonoFonts) -> NixExpr {
135 match mono.nerd_font_package_attr {
136 Some(attr) => raw(format!("pkgs.nerd-fonts.{attr}")),
137 None => NixExpr::Null,
138 }
139}
140
141fn italic_pkg(mono: &MonoFonts) -> NixExpr {
142 match mono.italic_package_attr {
143 Some(attr) => raw(format!("pkgs.{attr}")),
144 None => NixExpr::Null,
145 }
146}
147
148fn italic_style_name(s: ItalicStyle) -> &'static str {
149 match s {
150 ItalicStyle::MatchesPrimary => "matches_primary",
151 ItalicStyle::Calligraphic => "calligraphic",
152 ItalicStyle::Cursive => "cursive",
153 }
154}
155
156#[cfg(test)]
157mod tests {
158 use super::*;
159
160 #[test]
161 fn output_is_deterministic() {
162 let t = TokenSet::pleme();
163 assert_eq!(render(&t), render(&t));
164 }
165
166 #[test]
167 fn output_contains_all_canonical_slots() {
168 let out = render(&TokenSet::pleme());
169 for slot in [
170 "primary",
171 "italic",
172 "bold",
173 "symbols",
174 "emoji",
175 "fallback_chain",
176 ] {
177 assert!(
178 out.contains(&format!("{slot} = ")),
179 "missing slot {slot}\n{out}"
180 );
181 }
182 }
183
184 #[test]
185 fn primary_pins_jetbrains_mono_nerd_font_with_correct_package() {
186 let out = render(&TokenSet::pleme());
187 assert!(out.contains("name = \"JetBrainsMono Nerd Font\""));
188 assert!(out.contains("package = pkgs.nerd-fonts.jetbrains-mono"));
189 }
190
191 #[test]
192 fn italic_carries_style_intent() {
193 let out = render(&TokenSet::pleme());
194 assert!(out.contains("style_intent = \"matches_primary\""));
198 }
199
200 #[test]
201 fn symbols_slot_points_at_symbols_only_nerd_font() {
202 let out = render(&TokenSet::pleme());
203 assert!(out.contains("package = pkgs.nerd-fonts.symbols-only"));
204 }
205
206 #[test]
207 fn header_documents_provenance() {
208 let out = render(&TokenSet::pleme());
209 assert!(out.contains("ishou-render::fleet_fonts"));
210 assert!(out.contains("typography.rs"));
211 }
212
213 #[test]
214 fn fallback_chain_is_emitted_in_order() {
215 let out = render(&TokenSet::pleme());
216 let ts = TokenSet::pleme();
217 for name in ts.typography.mono_fonts.fallback {
218 assert!(
219 out.contains(&format!("\"{name}\"")),
220 "missing fallback {name}"
221 );
222 }
223 }
224
225 #[test]
226 fn output_starts_with_pkgs_lambda() {
227 let out = render(&TokenSet::pleme());
228 let body = out.lines().find(|l| !l.starts_with('#')).unwrap_or("");
231 assert_eq!(body, "{ pkgs }:");
232 }
233}