use crate::nix_ast::{AttrEntry, NixFile, attrset, str_};
use ishou_tokens::Rgb;
#[derive(Debug, Clone, Copy)]
pub struct SchemeMeta {
pub name: &'static str,
pub author: &'static str,
pub variant: &'static str,
pub slug: &'static str,
}
fn slot_hex(rgb: Rgb) -> String {
format!("{:02x}{:02x}{:02x}", rgb.r, rgb.g, rgb.b)
}
#[must_use]
pub fn render(
meta: &SchemeMeta,
slots: &[(&'static str, Rgb)],
source_of_truth: &str,
consumed_as: &str,
) -> String {
let mut entries: Vec<AttrEntry> = vec![
AttrEntry::new("system", str_("base16")),
AttrEntry::new("name", str_(meta.name)),
AttrEntry::new("author", str_(meta.author)),
AttrEntry::new("variant", str_(meta.variant)),
AttrEntry::new("slug", str_(meta.slug)),
];
entries.extend(
slots
.iter()
.map(|(slot, rgb)| AttrEntry::new(*slot, str_(slot_hex(*rgb)))),
);
NixFile::new(
[
"Generated by ishou-render::base16_nix — DO NOT EDIT".to_string(),
format!("Source of truth: {source_of_truth}"),
"Architecture: pleme-io/theory/THEME-ARCHITECTURE.md".to_string(),
String::new(),
"base16 as an ALREADY-PARSED attrset. Consumed as:".to_string(),
format!(" {consumed_as}"),
String::new(),
"The `{ yaml = <derivation>; }` form this replaces made stylix".to_string(),
"readFile a derivation — an IFD — which forced a linux build during".to_string(),
"evaluation and left every NixOS node unevaluable from a Mac.".to_string(),
],
attrset(entries),
)
.render()
}
#[cfg(test)]
mod tests {
use super::*;
const META: SchemeMeta = SchemeMeta {
name: "Test",
author: "pleme-io",
variant: "dark",
slug: "test",
};
fn slots() -> Vec<(&'static str, Rgb)> {
vec![("base00", Rgb::new(0x2e, 0x34, 0x40))]
}
#[test]
fn hex_is_lowercase_and_unprefixed() {
let out = render(&META, &slots(), "src", "expr");
assert!(out.contains(r#"base00 = "2e3440";"#), "got:\n{out}");
assert!(!out.contains("\"#2e3440\""), "hex must not carry a `#`:\n{out}");
}
#[test]
fn every_metadata_field_is_emitted() {
let out = render(&META, &slots(), "src", "expr");
for key in ["system", "name", "author", "variant", "slug"] {
assert!(out.contains(&format!("{key} = ")), "missing {key} in:\n{out}");
}
}
#[test]
fn output_is_deterministic() {
assert_eq!(
render(&META, &slots(), "src", "expr"),
render(&META, &slots(), "src", "expr")
);
}
}
#[cfg(test)]
mod committed_artifacts {
const VELLUM: &str = include_str!("../../../rendered/stylix-base16-vellum.nix");
const NORD: &str = include_str!("../../../rendered/stylix-base16-nord-dark.nix");
#[test]
fn vellum_matches_its_generator() {
assert_eq!(
crate::vellum::render_base16_nix(),
VELLUM,
"rendered/stylix-base16-vellum.nix is STALE. Regenerate:\n \
cargo run -p ishou-cli -- render --target stylix-vellum-nix \
> rendered/stylix-base16-vellum.nix"
);
}
#[test]
fn nord_matches_its_generator() {
assert_eq!(
crate::stylix::render_nix(&ishou_tokens::TokenSet::pleme()),
NORD,
"rendered/stylix-base16-nord-dark.nix is STALE. Regenerate:\n \
cargo run -p ishou-cli -- render --target stylix-nord-nix \
> rendered/stylix-base16-nord-dark.nix"
);
}
#[test]
fn neither_artifact_reintroduces_the_ifd_shape() {
for (name, body) in [("vellum", VELLUM), ("nord", NORD)] {
let code: String = body
.lines()
.filter(|l| !l.trim_start().starts_with('#'))
.collect::<Vec<_>>()
.join("\n");
assert!(
!code.contains("yaml ="),
"{name} reintroduced the `{{ yaml = <drv>; }}` IFD shape"
);
assert!(
code.contains("base00 = \""),
"{name} is not a parsed colour attrset"
);
}
}
}