diff --git a/Cargo.lock b/Cargo.lock index 523cfe8..7730a42 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -99,12 +99,6 @@ version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74f37166d7d48a0284b99dd824694c26119c700b53bf0d1540cdb147dbdaaf13" -[[package]] -name = "arraydeque" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" - [[package]] name = "atom_syndication" version = "0.12.4" @@ -749,18 +743,6 @@ dependencies = [ "phf 0.11.2", ] -[[package]] -name = "gray_matter" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31ee6a6070bad7c953b0c8be9367e9372181fed69f3e026c4eb5160d8b3c0222" -dependencies = [ - "serde", - "serde_json", - "toml", - "yaml-rust2", -] - [[package]] name = "h2" version = "0.3.26" @@ -820,15 +802,6 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" -[[package]] -name = "hashlink" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" -dependencies = [ - "hashbrown 0.14.5", -] - [[package]] name = "headers" version = "0.3.9" @@ -2263,15 +2236,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "toml" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "serde", -] - [[package]] name = "tower-service" version = "0.3.3" @@ -2701,17 +2665,6 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" -[[package]] -name = "yaml-rust2" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8902160c4e6f2fb145dbe9d6760a75e3c9522d8bf796ed7047c85919ac7115f8" -dependencies = [ - "arraydeque", - "encoding_rs", - "hashlink", -] - [[package]] name = "yoke" version = "0.7.4" @@ -2810,7 +2763,6 @@ dependencies = [ "fs_extra", "futures", "grass", - "gray_matter", "handlebars", "hotwatch", "itertools", diff --git a/Cargo.toml b/Cargo.toml index dace334..2af6611 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,6 @@ eyre = "0.6" fs_extra = "1.2" futures = {version = "0.3", optional = true} grass = {version = "0.13", default-features = false} -gray_matter = "0.2" handlebars = "6" hotwatch = {version = "0.5", optional = true} itertools = "0.13" diff --git a/site/pages/click.md b/site/pages/click.md index 2bdd7e0..4997b3c 100644 --- a/site/pages/click.md +++ b/site/pages/click.md @@ -6,5 +6,7 @@ embed: title: click site_name: zyl.gay description: click click click -extra: click +extra: + name: basic + template: extras/click --- diff --git a/site/pages/index.md b/site/pages/index.md index 4b7f110..d9b37a8 100644 --- a/site/pages/index.md +++ b/site/pages/index.md @@ -1,5 +1,9 @@ --- -extra: index +extra: + name: resource-list-outside + template: extras/index-injection + resource: blog + count: 3 --- # zyl is gay diff --git a/src/builder.rs b/src/builder.rs index bede56e..b81b2a0 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -3,7 +3,6 @@ use std::{collections::HashMap, path::PathBuf}; use eyre::{eyre, Context, OptionExt}; -use gray_matter::{engine::YAML, Matter}; use handlebars::Handlebars; use lol_html::{element, html_content::ContentType, HtmlRewriter, Settings}; use pulldown_cmark::{Options, Parser}; @@ -32,8 +31,6 @@ struct TemplateData<'a, T> { /// Struct used to build the site. pub struct SiteBuilder<'a> { - /// The matter instance used to extract front matter. - pub(crate) matter: Matter, /// The Handlebars registry used to render templates. pub(crate) reg: Handlebars<'a>, /// The site info used to build the site. @@ -59,7 +56,6 @@ impl<'a> SiteBuilder<'a> { } Self { - matter: Matter::new(), reg: Handlebars::new(), resource_builders: HashMap::new(), site, @@ -257,16 +253,14 @@ impl<'a> SiteBuilder<'a> { /// Helper to build a page without writing it to disk. pub fn build_page_raw_with_extra_data( &self, - page_metadata: PageMetadata, + mut page_metadata: PageMetadata, page_html: &str, extra_data: T, ) -> eyre::Result where T: Serialize, { - let extra = page_metadata - .extra - .and_then(|extra| crate::extras::get_extra(&extra)); + let extra = page_metadata.extra.take(); let title = match &page_metadata.title { Some(page_title) => format!("{} / {}", self.site.config.title, page_title), @@ -293,8 +287,10 @@ impl<'a> SiteBuilder<'a> { // Modify HTML output let mut out = self.rewrite_html(out)?; - if let Some(extra) = extra { - out = extra.handle(out, self)?; + if let Some(data) = extra { + if let Some(extra) = crate::extras::get_extra(&data.name) { + out = extra.handle(out, self, &data)?; + } } if !self.serving { @@ -310,19 +306,13 @@ impl<'a> SiteBuilder<'a> { let input = std::fs::read_to_string(page_path) .with_context(|| format!("Failed to read page at {}", page_path.display()))?; - let page = self.matter.parse(&input); - - let page_metadata = if let Some(data) = page.data { - data.deserialize()? - } else { - PageMetadata::default() - }; + let page = crate::frontmatter::FrontMatter::parse(input)?; let parser = Parser::new_ext(&page.content, Options::all()); let mut page_html = String::new(); pulldown_cmark::html::push_html(&mut page_html, parser); - let out = self.build_page_raw(page_metadata, &page_html)?; + let out = self.build_page_raw(page.data.unwrap_or_default(), &page_html)?; let out_path = self.build_path.join(page_name).with_extension("html"); std::fs::create_dir_all(out_path.parent().unwrap()) diff --git a/src/extras.rs b/src/extras.rs index 54ceb5d..be5611c 100644 --- a/src/extras.rs +++ b/src/extras.rs @@ -1,32 +1,58 @@ use lol_html::{element, RewriteStrSettings}; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use crate::{builder::SiteBuilder, resource::ResourceTemplateData}; +/// Types of extras. #[derive(Debug)] pub enum Extra { - Basic(&'static str), - HtmlModification(fn(page: String, builder: &SiteBuilder) -> eyre::Result), + /// Simply appends to the page within content. + Basic, + /// May modify the HTML output in any way. + HtmlModification( + fn(page: String, builder: &SiteBuilder, data: &ExtraData) -> eyre::Result, + ), } impl Extra { /// runs the handler for the extra - pub fn handle(&self, page: String, builder: &SiteBuilder) -> eyre::Result { + pub fn handle( + &self, + page: String, + builder: &SiteBuilder, + data: &ExtraData, + ) -> eyre::Result { + #[derive(Debug, Deserialize)] + struct BasicData { + template: String, + } + match self { - Self::Basic(template) => { - let content = builder.reg.render(template, &())?; + Self::Basic => { + let data: BasicData = serde_yml::from_value(data.inner.clone())?; + let content = builder.reg.render(&data.template, &())?; append_to(&page, &content, "main.page") } - Self::HtmlModification(f) => (f)(page, builder), + Self::HtmlModification(f) => (f)(page, builder, data), } } } +/// Data for extras. +#[derive(Debug, Deserialize)] +pub struct ExtraData { + /// The name of the extra to run. + pub name: String, + /// The inner data for the extra. + #[serde(flatten)] + pub inner: serde_yml::Value, +} + /// Gets the extra for the given value. pub fn get_extra(extra: &str) -> Option { match extra { - "index" => Some(Extra::HtmlModification(index)), - "click" => Some(Extra::Basic("extras/click")), + "basic" => Some(Extra::Basic), + "resource-list-outside" => Some(Extra::HtmlModification(resource_list_outside)), _ => None, } } @@ -46,22 +72,35 @@ fn append_to(page: &str, content: &str, selector: &str) -> eyre::Result } /// Extra to add a sidebar to the index page with recent blog posts on it. -fn index(page: String, builder: &SiteBuilder) -> eyre::Result { +fn resource_list_outside( + page: String, + builder: &SiteBuilder, + data: &ExtraData, +) -> eyre::Result { + #[derive(Debug, Deserialize)] + struct ResourceListData { + template: String, + resource: String, + count: usize, + } + #[derive(Debug, Serialize)] - struct SidebarTemplateData<'r> { + struct ResourceListTemplateData<'r> { resources: Vec>, } - let sidebar = builder.reg.render( - "extras/index-injection", - &SidebarTemplateData { + let data: ResourceListData = serde_yml::from_value(data.inner.clone())?; + + let resource_list = builder.reg.render( + &data.template, + &ResourceListTemplateData { resources: builder .resource_builders - .get("blog") - .expect("missing blog builder") + .get(&data.resource) + .ok_or_else(|| eyre::eyre!("missing resource builder: {}", data.resource))? .loaded_metadata .iter() - .take(3) + .take(data.count) .map(|(id, v)| ResourceTemplateData { resource: v, id: id.clone(), @@ -71,5 +110,5 @@ fn index(page: String, builder: &SiteBuilder) -> eyre::Result { }, )?; - append_to(&page, &sidebar, "#content") + append_to(&page, &resource_list, "#content") } diff --git a/src/frontmatter.rs b/src/frontmatter.rs new file mode 100644 index 0000000..b43307d --- /dev/null +++ b/src/frontmatter.rs @@ -0,0 +1,32 @@ +use serde::de::DeserializeOwned; + +/// Very basic YAML front matter parser. +#[derive(Debug)] +pub struct FrontMatter { + /// The content past the front matter. + pub content: String, + /// The front matter found, if any. + pub data: Option, +} + +impl FrontMatter +where + T: DeserializeOwned, +{ + /// Parses the given input for front matter. + pub fn parse(input: String) -> eyre::Result { + if input.starts_with("---\n") { + if let Some((frontmatter, content)) = input[3..].split_once("---\n") { + let data = serde_yml::from_str(frontmatter)?; + return Ok(Self { + content: content.to_string(), + data, + }); + } + } + Ok(Self { + content: input, + data: None, + }) + } +} diff --git a/src/lib.rs b/src/lib.rs index cb72b23..b1e8496 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ mod builder; mod extras; +mod frontmatter; mod link_list; mod resource; #[cfg(feature = "serve")] @@ -11,6 +12,7 @@ use std::{ path::{Path, PathBuf}, }; +use extras::ExtraData; use eyre::Context; use rayon::prelude::*; use resource::{EmbedMetadata, ResourceBuilderConfig}; @@ -73,7 +75,8 @@ pub struct PageMetadata { #[serde(default)] pub styles: Vec, /// The extra stuff to run for the page, if any. - pub extra: Option, + #[serde(default)] + pub extra: Option, } /// Struct containing information about the site. diff --git a/src/resource.rs b/src/resource.rs index f80839f..a4797d5 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -10,7 +10,7 @@ use rss::{validation::Validate, ChannelBuilder, ItemBuilder}; use serde::{Deserialize, Serialize, Serializer}; use time::{format_description::well_known::Rfc2822, OffsetDateTime}; -use crate::{builder::SiteBuilder, link_list::Link, PageMetadata}; +use crate::{builder::SiteBuilder, frontmatter::FrontMatter, link_list::Link, PageMetadata}; /// Source base path for resources. pub const RESOURCES_PATH: &str = "resources"; @@ -194,21 +194,22 @@ impl ResourceBuilder { let id = Self::get_id(path); let input = std::fs::read_to_string(path)?; - let mut page = builder - .matter - .parse_with_struct::(&input) - .ok_or_else(|| eyre::anyhow!("Failed to parse resource front matter"))?; + let page = FrontMatter::::parse(input) + .wrap_err_with(|| eyre::eyre!("Failed to parse resource front matter"))?; let parser = Parser::new_ext(&page.content, Options::all()); let mut html = String::new(); pulldown_cmark::html::push_html(&mut html, parser); - page.data.content = html; - if let Some(cdn_file) = page.data.cdn_file { - page.data.cdn_file = Some(builder.site.config.cdn_url(&cdn_file)?.to_string()); + let mut data = page + .data + .ok_or_else(|| eyre::eyre!("missing front matter for file at {path:?}"))?; + data.content = html; + if let Some(cdn_file) = data.cdn_file { + data.cdn_file = Some(builder.site.config.cdn_url(&cdn_file)?.to_string()); } - Ok((id, page.data)) + Ok((id, data)) } /// Loads all resource metadata from the given config.