Working generator & added whole site

This commit is contained in:
Thomas
2021-03-28 15:54:59 +02:00
commit 19123fa85a
82 changed files with 1785 additions and 0 deletions

123
src/generator/generate.rs Normal file
View File

@@ -0,0 +1,123 @@
use std::fs::{ File, create_dir_all };
use std::path::Path;
use handlebars::Handlebars;
use serde::Serialize;
use super::loader;
use super::models::post::Post;
use super::models::section::Section;
#[derive(Serialize)]
struct TemplateData<'a> {
section: &'a Section,
post: Option<&'a Post>,
}
pub fn landing_page() {
let mut handlebars = Handlebars::new();
match handlebars.register_template_file("landing", "./templates/landing.hbs") {
Err(error) => panic!("Could not load landing template: {}", error),
Ok(_) => (),
};
match handlebars.register_template_file("header", "./templates/header.hbs") {
Err(error) => panic!("Could not load header template: {}", error),
Ok(_) => (),
};
let data = "";
let mut output_file = match File::create("./generated/index.html") {
Err(error) => panic!("Could not create landing index: {}", error),
Ok(file) => file,
};
match handlebars.render_to_write("landing", &data, &mut output_file) {
Err(error) => panic!("Error rendering landing: {}", error),
Ok(_) => (),
};
}
pub fn sections() {
let sections = loader::sections();
let mut handlebars = Handlebars::new();
load_templates(&mut handlebars);
for section in &sections {
let section_path = Path::new("./generated").join(section.url.to_owned());
match create_dir_all(section_path) {
Ok(_) => println!("[GEN] {} -> Created folder at url {}", section.title, section.url),
Err(error) => panic!("Error creating section folder: {}", error),
};
// Generate posts
for post in &section.posts {
let data = TemplateData {
post: Some(post),
section: section,
};
let post_path = Path::new("./generated").join(section.url.to_owned()).join(post.url.to_owned());
let mut output_file = match File::create(post_path) {
Err(error) => panic!("Could not create post: {}", error),
Ok(file) => file,
};
match handlebars.render_to_write("base", &data, &mut output_file) {
Err(error) => panic!("Error rendering post: {}", error),
Ok(_) => println!("[GEN] {} -> Rendered post {}", section.title, post.title),
};
}
// Generate section index
let data = TemplateData {
post: None,
section: section,
};
let index_path = Path::new("./generated").join(section.url.to_owned()).join("index.html");
let mut output_file = match File::create(index_path) {
Err(error) => panic!("Could not create section index: {}", error),
Ok(file) => file,
};
match handlebars.render_to_write("base_section", &data, &mut output_file) {
Err(error) => panic!("Error rendering post: {}", error),
Ok(_) => println!("[GEN] {} -> Rendered index", section.title),
};
}
}
fn load_templates(handlebars: &mut Handlebars) {
match handlebars.register_template_file("base", "./templates/base.hbs") {
Err(error) => panic!("Could not load base template: {}", error),
Ok(_) => (),
};
match handlebars.register_template_file("base_section", "./templates/base_section.hbs") {
Err(error) => panic!("Could not load base template: {}", error),
Ok(_) => (),
};
match handlebars.register_template_file("header", "./templates/header.hbs") {
Err(error) => panic!("Could not load header template: {}", error),
Ok(_) => (),
};
match handlebars.register_template_file("menu", "./templates/menu.hbs") {
Err(error) => panic!("Could not load menu template: {}", error),
Ok(_) => (),
};
match handlebars.register_template_file("submenu", "./templates/submenu.hbs") {
Err(error) => panic!("Could not load submenu template: {}", error),
Ok(_) => (),
};
match handlebars.register_template_file("article", "./templates/article.hbs") {
Err(error) => panic!("Could not load article template: {}", error),
Ok(_) => (),
};
}

73
src/generator/loader.rs Normal file
View File

@@ -0,0 +1,73 @@
use std::fs::File;
use std::fs::DirEntry;
use std::fs::read_dir;
use std::path::Path;
use crate::markdown::marky::Marky;
use super::models::section::Section;
use super::models::post::Post;
pub fn sections() -> Vec<Section> {
let dir = match read_dir("./raws") {
Err(error) => panic!("Could read raws directory: {}", error),
Ok(dir) => dir,
};
let mut sections: Vec<Section> = Vec::new();
for entry in dir {
let entry = match entry {
Err(error) => panic!("Could not read file: {}", error),
Ok(entry) => entry,
};
if entry.path().is_file() {
} else {
sections.push(load_section(&entry));
}
}
return sections;
}
fn load_section(dir_entry: &DirEntry) -> Section {
let dir = match read_dir(dir_entry.path()) {
Err(error) => panic!("Could read raws sub directory: {}", error),
Ok(dir) => dir,
};
let mut section_posts: Vec<Post> = Vec::new();
for entry in dir {
let entry = match entry {
Err(error) => panic!("Could not read file: {}", error),
Ok(entry) => entry.path(),
};
if entry.is_file() {
section_posts.push(load_post(&entry));
}
}
let section_name = dir_entry.file_name().into_string().unwrap();
let section_url = section_name.to_lowercase().trim().replace(" ", "_");
println!("[LOADER] Loaded section {} with {} posts", section_name, section_posts.len());
return Section {
title: section_name,
url: section_url,
posts: section_posts,
}
}
fn load_post(path: &Path) -> Post {
let file = match File::open(path) {
Err(error) => panic!("Could not read file: {}", error),
Ok(file) => file,
};
let marky = Marky::from_file(&file);
return Post::from_marky(marky);
}

3
src/generator/mod.rs Normal file
View File

@@ -0,0 +1,3 @@
pub mod generate;
pub mod loader;
pub mod models;

View File

@@ -0,0 +1,2 @@
pub mod post;
pub mod section;

View File

@@ -0,0 +1,34 @@
use serde::Serialize;
use markdown::to_html;
use crate::markdown::marky::Marky;
#[derive(Serialize)]
pub struct Post {
pub title: String,
pub date: String,
pub url: String,
pub description: String,
pub tags: Vec<String>,
pub body: String,
pub is_year_only: bool,
}
impl Post {
pub fn from_marky(marky: Marky) -> Post {
Post {
title: marky.metadata.get("title").map_or(String::from("none"), String::from),
date: marky.metadata.get("date").map_or(String::from("none"), String::from),
url: marky.metadata.get("url").map_or(String::from("none"), String::from),
description: marky.metadata.get("description").map_or(String::from("none"), String::from),
tags: tags_string_to_vec(marky.metadata.get("tags").map_or(String::from("none"), String::from)),
is_year_only: marky.metadata.get("yearonly").map_or(false, |a| a == "true"),
body: to_html(&marky.content),
}
}
}
fn tags_string_to_vec(tags: String) -> Vec<String> {
return tags.split(",").map(String::from).collect();
}

View File

@@ -0,0 +1,10 @@
use serde::Serialize;
use super::post::Post;
#[derive(Serialize)]
pub struct Section {
pub title: String,
pub url: String,
pub posts: Vec<Post>,
}

21
src/main.rs Normal file
View File

@@ -0,0 +1,21 @@
use std::fs;
mod generator;
mod markdown;
use generator::generate;
fn main() {
println!("[MAIN] Starting...");
check_generated_folder();
generate::landing_page();
generate::sections();
println!("[MAIN] Generation completed !");
}
fn check_generated_folder() {
match fs::create_dir_all("./generated") {
Ok(_) => println!("[MAIN] Folder generated created"),
Err(error) => println!("Error creating generated folder: {}", error),
};
}

40
src/markdown/converter.rs Normal file
View File

@@ -0,0 +1,40 @@
use super::marky::Marky;
pub fn marky_to_html (marky: Marky) -> String {
let mut html = String::new();
for line in marky.content.lines() {
if line.starts_with("####") {
html.push_str("<h4>");
html.push_str(&line.to_owned());
html.push_str("</h4>");
} else if line.starts_with("###") {
html.push_str("<h3>");
html.push_str(&line.to_owned());
html.push_str("</h3>");
} else if line.starts_with("##") {
html.push_str("<h2>");
html.push_str(&line.to_owned());
html.push_str("</h2>");
} else if line.starts_with("#") {
html.push_str("<h1>");
html.push_str(&line.to_owned());
html.push_str("</h1>");
} else if line.starts_with("*") || line.starts_with("-") {
html.push_str("<h1>");
html.push_str(&line.to_owned());
html.push_str("</h1>");
} else {
html.push_str("<p>");
html.push_str(&line.to_owned());
html.push_str("</p>");
}
}
return html;
}

62
src/markdown/marky.rs Normal file
View File

@@ -0,0 +1,62 @@
use std::collections::HashMap;
use std::io::{BufRead, BufReader};
use std::fs::File;
pub struct Marky {
pub metadata: HashMap<String, String>,
pub content: String,
}
impl Marky {
pub fn from_file(file: &File) -> Marky {
let reader = BufReader::new(file);
let mut is_metadata = false;
let mut metadata = HashMap::new();
let mut content = String::new();
for line in reader.lines() {
let line = match line {
Err(error) => panic!("Could not read line: {}", error),
Ok(line) => line,
};
match line.trim() {
"---" => {
if metadata.len() != 0 && !is_metadata {
is_metadata = false;
content.push_str(&line);
} else {
is_metadata = !is_metadata;
}
}
_ => {
if is_metadata {
if let Some(metadata_entry) = line_to_metadata(line) {
metadata.insert(metadata_entry.0, metadata_entry.1);
}
} else {
content.push_str(&line);
content.push_str("\n");
}
},
}
}
Marky {
metadata,
content
}
}
}
fn line_to_metadata(line: String) -> Option<(String, String)> {
let separator_index = match line.find(":") {
None => return None,
Some(index) => index,
};
let (key, value) = line.split_at(separator_index);
return Some((String::from(key), String::from(value.trim_start_matches(":").trim())));
}

2
src/markdown/mod.rs Normal file
View File

@@ -0,0 +1,2 @@
pub mod converter;
pub mod marky;