v6/src/generator/tutorials.rs

178 lines
4.6 KiB
Rust

use super::posts::parse::Tag;
use super::util::slugify::slugify;
use super::util::templates::{filters, TemplateCommon};
use super::util::{from_frontmatter, output_rendered_template, word_count};
use super::{content_path, markdown};
use askama::Template;
use chrono::{DateTime, FixedOffset};
use serde::Deserialize;
use std::{fs, io::Read, path::PathBuf};
pub fn generate() {
let series = vec![
generate_series("forge-modding-1102", "Forge Mods for 1.10.2"),
generate_series("forge-modding-1112", "Forge Mods for 1.11.2"),
generate_series("forge-modding-112", "Forge Mods for 1.12"),
];
generate_index(&series);
}
fn generate_index(series: &[TutorialSeries]) {
output_rendered_template(
&TutorialsIndexTemplate { all_series: series },
"/tutorials/index.html",
)
.unwrap();
}
fn generate_series(slug: &'static str, name: &'static str) -> TutorialSeries {
let mut path = content_path("tutorials");
path.push(slug);
let posts = fs::read_dir(path)
.unwrap()
.map(|entry| {
let entry = entry.unwrap();
assert!(entry.file_type().unwrap().is_file());
parse_post(entry.path(), slug, name)
})
.collect::<Vec<_>>();
let series = TutorialSeries { slug, name, posts };
for post in series.posts.iter() {
generate_post(&series, post);
}
generate_series_index(&series);
series
}
fn generate_post(series: &TutorialSeries, post: &TutorialPost) {
output_rendered_template(
&TutorialPostTemplate { series, post },
format!("/tutorials/{}/{}/index.html", series.slug, post.slug),
)
.unwrap();
}
fn generate_series_index(series: &TutorialSeries) {
output_rendered_template(
&TutorialSeriesTemplate { series },
format!("/tutorials/{}/index.html", series.slug),
)
.unwrap();
}
fn parse_post(path: PathBuf, series_slug: &str, series_name: &str) -> TutorialPost {
let mut f = fs::File::open(&path).unwrap();
let mut buffer = String::new();
f.read_to_string(&mut buffer).unwrap();
TutorialPost::new(path, &buffer, series_slug, series_name)
}
struct TutorialSeries {
slug: &'static str,
name: &'static str,
posts: Vec<TutorialPost>,
}
impl TutorialSeries {
fn last_updated(&self) -> &DateTime<FixedOffset> {
&self
.posts
.iter()
.max_by_key(|p| p.metadata.date)
.unwrap()
.metadata
.date
}
}
struct TutorialPost {
content: String,
slug: String,
word_count: Option<u32>,
metadata: TutorialPostMetadata,
}
impl TutorialPost {
fn new(path: PathBuf, contents: &str, series_slug: &str, series_name: &str) -> Self {
let (mut metadata, rest_contents) =
from_frontmatter::<'_, TutorialPostMetadata>(contents).unwrap();
metadata.tags = Some(vec![Tag {
name: series_name.to_owned(),
slug: format!("tutorials/{}", series_slug),
}]);
let slug = metadata
.slug
.clone()
.unwrap_or_else(|| slugify(&metadata.title));
assert!(path.extension().unwrap().eq_ignore_ascii_case("md"));
let wc = word_count::markdown(rest_contents);
let mut buf = vec![];
markdown::render(rest_contents, &mut buf);
let html = String::from_utf8(buf).unwrap();
TutorialPost {
content: html,
slug,
word_count: Some(wc),
metadata,
}
}
}
#[derive(Deserialize)]
struct TutorialPostMetadata {
title: String,
date: DateTime<FixedOffset>,
slug: Option<String>,
tags: Option<Vec<Tag>>,
}
#[derive(Template)]
#[template(path = "layout/tutorial.html")]
struct TutorialPostTemplate<'a> {
series: &'a TutorialSeries,
post: &'a TutorialPost,
}
impl<'a> TemplateCommon for TutorialPostTemplate<'a> {}
impl<'a> TutorialPostTemplate<'a> {
fn permalink(&self) -> String {
format!("/tutorials/{}/{}/", self.series.slug, self.post.slug)
}
}
#[derive(Template)]
#[template(path = "layout/tutorial-series.html")]
struct TutorialSeriesTemplate<'a> {
series: &'a TutorialSeries,
}
impl<'a> TemplateCommon for TutorialSeriesTemplate<'a> {}
impl<'a> TutorialSeriesTemplate<'a> {
fn permalink(&self) -> String {
format!("/tutorials/{}/", self.series.slug)
}
}
#[derive(Template)]
#[template(path = "tutorials.html")]
struct TutorialsIndexTemplate<'a> {
all_series: &'a [TutorialSeries],
}
impl<'a> TemplateCommon for TutorialsIndexTemplate<'a> {}
impl<'a> TutorialsIndexTemplate<'a> {
fn permalink(&self) -> &'static str {
"/tutorials/"
}
}