Skip to content
This repository has been archived by the owner on Jun 9, 2024. It is now read-only.

Commit

Permalink
Add new recipes and organise by tags (#10)
Browse files Browse the repository at this point in the history
* Stub new recipes

* Tag recipes

* Begin parsing nodes

* Trying to deserialize yaml

* Add tags page
  • Loading branch information
ingalless authored Apr 23, 2024
1 parent 959e3f9 commit 5014631
Show file tree
Hide file tree
Showing 9 changed files with 187 additions and 8 deletions.
29 changes: 25 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ edition = "2021"
rocket = "0.5.0"
comrak = "0.21.0"
maud = { version = "0.26.0", features = ["rocket"] }
serde = { version = "1.0.198", features = ["std", "derive"] }
serde_yaml = "0.9.34"

[dev-dependencies]
tempdir = "0.3.7"
12 changes: 12 additions & 0 deletions recipes/broccoli-curry-with-gunpowder-potatoes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
servings: 2
difficulty: 1
tags:
- indian
- healthy
- vegetarian
---

# Broccoli Curry with Gunpower Potatoes

Pending recipe...and a better name...
3 changes: 3 additions & 0 deletions recipes/honey-soy-salmon.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
---
servings: 2
difficulty: 1
tags:
- asian
- healthy
---

# Honey Soy Salmon
Expand Down
10 changes: 10 additions & 0 deletions recipes/stuffed-aubergine-lamb-mince.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
servings: 2
difficulty: 1
tags:
- healthy
---

# Stuffed Aubergines

Pending recipe...
12 changes: 12 additions & 0 deletions recipes/sweet-and-sour-cauliflower.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
servings: 2
difficulty: 1
tags:
- asian
- healthy
- vegetarian
---

# Sweet and Sour Cauliflower

Pending recipe...
97 changes: 95 additions & 2 deletions src/compiler.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,82 @@
use std::path::Path;
use serde::Deserialize;
use std::{collections::HashMap, path::Path};

use comrak::{
nodes::{AstNode, NodeValue},
parse_document, Arena, Options,
};

use crate::{views, Recipe};

#[derive(Deserialize, Debug)]
struct Frontmatter {
tags: Vec<String>,
}

impl Frontmatter {
fn default() -> Self {
Self { tags: Vec::new() }
}
}

pub struct Compiler {
options: Options,
path: String,
}

fn process_node<'a>(node: &'a AstNode<'a>) -> Vec<String> {
let mut tags = vec![];
match node.data.borrow().value {
NodeValue::FrontMatter(ref text) => {
let raw = text.replace("---", "");
let frontmatter: Frontmatter =
serde_yaml::from_str(&raw).unwrap_or(Frontmatter::default());
for tag in frontmatter.tags {
tags.push(tag)
}
}
_ => (),
};

tags
}

fn extract_tags(options: &Options, content: &String) -> Vec<String> {
let arena = Arena::new();
let root = parse_document(&arena, &content, &options);
let mut tags: Vec<String> = vec![];

for c in root.children() {
for tag in process_node(&c) {
tags.push(tag);
}
}

return tags;
}

impl Compiler {
pub fn new(path: String) -> Self {
Self { path }
let mut options = Options::default();
options.parse.relaxed_tasklist_matching = true;
options.extension.tasklist = true;
options.extension.front_matter_delimiter = Some("---".into());

Self { path, options }
}

pub fn compile_recipes(self: &Self, recipes: Vec<Recipe>) -> Result<(), String> {
let mut tag_map: HashMap<String, Vec<String>> = HashMap::new();
for recipe in recipes {
for tag in extract_tags(&self.options, &recipe.content) {
match tag_map.get_mut(&tag) {
Some(v) => v.push(recipe.title.clone()),
None => {
tag_map.insert(tag, vec![recipe.title.clone()]);
}
}
}

let target_path = Path::new(&self.path).join(format!("{}.html", recipe.title));
let write_result = std::fs::write(&target_path, views::recipe(&recipe).into_string());
match write_result {
Expand All @@ -23,6 +87,35 @@ impl Compiler {
}
};
}
let tag_target_path = Path::new(&self.path).join("tags.html");
let write_result = std::fs::write(&tag_target_path, views::tags(&tag_map).into_string());
match write_result {
Ok(_) => println!("Wrote {}", tag_target_path.to_str().unwrap()),
Err(_) => {
println!("Failed to write tags page");
return Err("Failed to write tags page".into());
}
};
Ok(())
}
}

#[cfg(test)]
mod test {
use super::{extract_tags, Compiler};

#[test]
fn single() {
let compiler = Compiler::new("".into());
let content = "---
tags:
- asian
- curry
---
"
.to_string();
let expected_tags = vec![String::from("asian"), String::from("curry")];
let tags = extract_tags(&compiler.options, &content);
assert_eq!(expected_tags, tags);
}
}
10 changes: 9 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ impl Recipe {
}
}

#[get("/tags")]
fn tags() -> Result<(Status, (ContentType, String)), Status> {
let compiled_path = std::env::var("APP_COMPILED_PATH").unwrap_or("./compiled".into());
let path = Path::new(&compiled_path).join("tags.html");
let content = fs::read_to_string(path).unwrap_or(String::from(""));
Ok((Status::Ok, (ContentType::HTML, content)))
}

#[get("/recipe/<recipe>")]
fn read_recipe(recipe: &str) -> Result<(Status, (ContentType, String)), Status> {
let compiled_path = std::env::var("APP_COMPILED_PATH").unwrap_or("./compiled".into());
Expand Down Expand Up @@ -86,7 +94,7 @@ fn rocket() -> _ {
println!("{}", e);
}
}
rocket::build().mount("/", routes![index, read_recipe])
rocket::build().mount("/", routes![index, read_recipe, tags])
}

#[cfg(test)]
Expand Down
20 changes: 19 additions & 1 deletion src/views.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::collections::HashMap;

use maud::{html, Markup};

use crate::Recipe;
Expand All @@ -6,8 +8,9 @@ fn layout(rest: Markup) -> Markup {
html! {
(head())
body class="m-8" {
nav class="my-4 w-full border-b print:hidden" {
nav class="my-4 space-x-3 w-full border-b print:hidden" {
a href="/" class="text-blue-500" { "Home" }
a href="/tags" class="text-blue-500" { "Browse by tags" }
}
main {
(rest)
Expand Down Expand Up @@ -40,6 +43,21 @@ pub fn index(recipes: Vec<Recipe>) -> Markup {
})
}

pub fn tags(tags: &HashMap<String, Vec<String>>) -> Markup {
layout(html! {
section class="prose-sm" {
@for (tag, recipes) in tags {
h2 { (tag) }
ul {
@for recipe in recipes {
li { a class="text-blue-500" href=(format!("/recipe/{}", recipe)) { (recipe) } }
}
}
}
}
})
}

pub fn recipe(recipe: &Recipe) -> Markup {
layout(html! {
article class="prose-sm" {
Expand Down

0 comments on commit 5014631

Please sign in to comment.