diff --git a/whiskers/examples/nested.hbs b/whiskers/examples/nested.hbs index 7468a589..b3491a4c 100644 --- a/whiskers/examples/nested.hbs +++ b/whiskers/examples/nested.hbs @@ -1,13 +1,19 @@ --- -accent: {{flavors.frappe.mauve}} +# You can define custom variables for each flavor here +accent: "{{mauve}}" +# Root context +parent: "shouldn't require a flavour" --- -How do we want to handle frontmatter? -It makes sense when a single flavour is being rendered, but not when multiple are... at the minute anyways. +# This doesn't currently work when it _should_ +Parent: {{parent}} {{#each flavors as | flavor flavour | }} -- **{{flavour}}** - {{ #each colors as | color colorName | }} - - **{{colorName}}** - {{/each}} +# {{titlecase flavour}} + - Parent: {{../parent}} + - Chosen Accent: **{{accent}}** + - Every Color: + {{ #each colors as | color colorName | }} + - **{{colorName}}** + {{/each}} {{/each}} diff --git a/whiskers/src/frontmatter.rs b/whiskers/src/frontmatter.rs index 27de9b93..5f82533b 100644 --- a/whiskers/src/frontmatter.rs +++ b/whiskers/src/frontmatter.rs @@ -16,6 +16,30 @@ fn split(template: &str) -> Option<(&str, &str)> { .map(|(a, b)| (a.trim(), b.trim())) } +#[must_use] +#[allow(clippy::missing_panics_doc)] // panic here implies an internal issue +pub fn render_and_parse_all<'a>( + template: &'a str, + reg: &Handlebars, + ctx: &Value, +) -> (&'a str, Vec>) { + let (_, content) = split(template).unwrap_or(("", template)); + + let frontmatter = ctx + .as_object() + .expect("context is an object") + .values() + .map(|v| render_and_parse(template, reg, v).1) + .collect(); + + // TODO: The biggest problem is that the entire frontmatter is rendered under each flavor, + // which means that you can't define any variables in the root context. + // Try running the `nested.hbs` file with flavor `all` to see what I mean. + dbg!(&frontmatter); + + (content, frontmatter) +} + #[must_use] pub fn render_and_parse<'a>( template: &'a str, diff --git a/whiskers/src/main.rs b/whiskers/src/main.rs index 454d5e33..1c0082c4 100644 --- a/whiskers/src/main.rs +++ b/whiskers/src/main.rs @@ -2,7 +2,6 @@ // we like truncating u32s into u8s around here #![allow(clippy::cast_possible_truncation)] use std::{ - clone::Clone, env, fs, io::Write, path::{Path, PathBuf}, @@ -51,6 +50,8 @@ struct Override { pub value: serde_json::Value, } +type Map = serde_json::Map; + fn parse_override(s: &str) -> Result { let kvpair = s.split_once('='); if let Some((key, value)) = kvpair { @@ -106,12 +107,36 @@ fn overrides_to_map(overrides: Vec) -> serde_json::Map>, + overrides: &[Override], +) -> serde_json::Value { + let ctx = ctx.as_object().expect("ctx is an object").clone(); + + let flavors: Map = ctx + .into_iter() + .zip(frontmatter) + .map(|((name, ctx), frontmatter)| { + // TODO: How do we handle command line overrides for all flavors? + let merged_ctx = merge_contexts(ctx, frontmatter, overrides.into()); + (name, merged_ctx) + }) + .collect(); + + // TODO: The biggest problem is that the entire frontmatter is rendered under each flavor, + // which means that you can't define any variables in the root context. + // Try running the `nested.hbs` file with flavor `all` to see what I mean. + let merged = serde_json::json!({ "flavors": flavors }); + + serde_json::to_value(merged).expect("merged context is serializable") +} + fn merge_contexts( ctx: serde_json::Value, frontmatter: Option, overrides: Vec, ) -> serde_json::Value { - type Map = serde_json::Map; let mut merged = Map::new(); let overrides = contextualize_overrides(overrides, &ctx); @@ -150,17 +175,21 @@ fn main() -> Result<()> { .expect("template_path is guaranteed to be set"); let flavor = args.flavor.expect("flavor is guaranteed to be set"); + let is_flavor_all = matches!(flavor, Flavor::All); let reg = template::make_registry(); - let ctx = if matches!(flavor, Flavor::All) { - template::make_context_all() + let (content, ctx) = if is_flavor_all { + let ctx = template::make_context_all(); + let (content, frontmatter) = frontmatter::render_and_parse_all(template, ®, &ctx); + let merged_ctx = merge_contexts_all(&ctx, frontmatter, &args.overrides); + (content, merged_ctx) } else { - template::make_context(flavor.into()) + let ctx = template::make_context(flavor.into()); + let (content, frontmatter) = frontmatter::render_and_parse(template, ®, &ctx); + let merged_ctx = merge_contexts(ctx, frontmatter, args.overrides); + (content, merged_ctx) }; - let (content, frontmatter) = frontmatter::render_and_parse(template, ®, &ctx); - - let ctx = merge_contexts(ctx, frontmatter, args.overrides); let result = reg .render_template(content, &ctx) diff --git a/whiskers/src/template.rs b/whiskers/src/template.rs index aed779f6..dab475b1 100644 --- a/whiskers/src/template.rs +++ b/whiskers/src/template.rs @@ -1,5 +1,3 @@ -use std::collections::HashMap; - use handlebars::Handlebars; use handlebars::HelperDef; use indexmap::IndexMap; @@ -229,14 +227,11 @@ fn color_priority(color: &str) -> u32 { #[must_use] #[allow(clippy::missing_panics_doc)] // panic here implies an internal issue pub fn make_context_all() -> serde_json::Value { - let mut flavors: IndexMap<&str, serde_json::Value> = catppuccin::Flavour::into_iter() - .map(|f| (f.name(), make_context(f))) + let mut ctx: IndexMap = catppuccin::Flavour::into_iter() + .map(|f| (f.name().into(), make_context(f))) .collect(); - flavors.sort_by(|a, _, b, _| flavor_priority(a).cmp(&flavor_priority(b))); - - let context = HashMap::from([("flavors", flavors)]); - - serde_json::to_value(context).expect("flavours can be serialized") + ctx.sort_by(|a, _, b, _| flavor_priority(a).cmp(&flavor_priority(b))); + serde_json::to_value(ctx).expect("context is serializable into json") } #[must_use]