The admitted icons as Kotlin data, so the app draws them crisp at any size in their own colours.

Each icon is two strings: its size * size pixels (. empty, otherwise an index) and its palette (six hex digits per index). Strings are constants, so a couple of hundred icons do not grow a method past the JVM's limit the way array literals would.

7use crate::IconId;
8use crate::raster::Icon;

The Kotlin file for every admitted icon. svg returns the text of an icon's file, so this stays free of the file system.

12pub fn generate(svg: impl Fn(&IconId) -> Result<String, String>) -> Result<String, String> {
13    let mut out = String::from(
14        "// Generated by `cargo run -p whiskers-icons --bin gen-kotlin` (tools/build-core.sh). Do not edit.\n\
15         // The icons are shuqikhor/pixel-icons by shuqikhor, restricted to crates/whiskers-icons/allowlist.txt.\n\
16         package app.whiskers.art\n\n\
17         /** An admitted pixel icon: `pixels` has `size * size` characters, `.` for empty or an index into `palette`. */\n\
18         class PixelIconData(val size: Int, val pixels: String, val palette: String)\n\n\
19         object PixelIconSet {\n    val all: Map<String, PixelIconData> = mapOf(\n",
20    );
21    for id in IconId::all() {
22        let text = svg(&id).map_err(|e| format!("{id}: {e}"))?;
23        let icon = Icon::parse(&text).map_err(|e| format!("{id}: {e}"))?;
24        let colours = icon.colours();
25        if colours.len() > 36 {
26            return Err(format!("{id}: more than 36 colours"));
27        }
28        let ascii = icon.ascii().replace('\n', "");
29        let palette: String = colours.iter().map(|c| format!("{:02X}{:02X}{:02X}", c[0], c[1], c[2])).collect();
30        out.push_str(&format!("        \"{id}\" to PixelIconData({}, \"{ascii}\", \"{palette}\"),\n", icon.size));
31    }
32    out.push_str("    )\n}\n");
33    Ok(out)
34}