Skip to content

Commit

Permalink
feat: allow frontmatter for each flavour
Browse files Browse the repository at this point in the history
  • Loading branch information
sgoudham committed Dec 20, 2023
1 parent b9b9c2b commit bd0c2b9
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 24 deletions.
20 changes: 13 additions & 7 deletions whiskers/examples/nested.hbs
Original file line number Diff line number Diff line change
@@ -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}}
24 changes: 24 additions & 0 deletions whiskers/src/frontmatter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Option<Value>>) {
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,
Expand Down
45 changes: 37 additions & 8 deletions whiskers/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -51,6 +50,8 @@ struct Override {
pub value: serde_json::Value,
}

type Map = serde_json::Map<String, serde_json::Value>;

fn parse_override(s: &str) -> Result<Override> {
let kvpair = s.split_once('=');
if let Some((key, value)) = kvpair {
Expand Down Expand Up @@ -106,12 +107,36 @@ fn overrides_to_map(overrides: Vec<Override>) -> serde_json::Map<String, serde_j
overrides.into_iter().map(|o| (o.key, o.value)).collect()
}

fn merge_contexts_all(
ctx: &serde_json::Value,
frontmatter: Vec<Option<serde_json::Value>>,
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<serde_json::Value>,
overrides: Vec<Override>,
) -> serde_json::Value {
type Map = serde_json::Map<String, serde_json::Value>;
let mut merged = Map::new();

let overrides = contextualize_overrides(overrides, &ctx);
Expand Down Expand Up @@ -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, &reg, &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, &reg, &ctx);
let merged_ctx = merge_contexts(ctx, frontmatter, args.overrides);
(content, merged_ctx)
};
let (content, frontmatter) = frontmatter::render_and_parse(template, &reg, &ctx);

let ctx = merge_contexts(ctx, frontmatter, args.overrides);

let result = reg
.render_template(content, &ctx)
Expand Down
13 changes: 4 additions & 9 deletions whiskers/src/template.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::collections::HashMap;

use handlebars::Handlebars;
use handlebars::HelperDef;
use indexmap::IndexMap;
Expand Down Expand Up @@ -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<String, serde_json::Value> = 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]
Expand Down

0 comments on commit bd0c2b9

Please sign in to comment.