From db1dcc070499c8d762dc638539596cde1d31e195 Mon Sep 17 00:00:00 2001 From: Erin Nova Date: Sun, 23 Jan 2022 16:38:44 -0500 Subject: [PATCH] Add database functionality, recipe pages --- Cargo.lock | 277 ++++++++++++++++++++++++++++++ Cargo.toml | 4 + index.html | 1 - src/io.rs | 139 ++++++++++++++- src/main.rs | 40 ++++- src/structures.rs | 167 ++++++++++++++++-- src/tests.rs | 5 + static/en/another-one!/index.html | 1 + static/en/full-recipe/index.html | 1 + static/en/index.html | 1 + static/en/recipe-test/index.html | 1 + style.css => static/en/style.css | 5 +- 12 files changed, 611 insertions(+), 31 deletions(-) delete mode 100644 index.html create mode 100644 static/en/another-one!/index.html create mode 100644 static/en/full-recipe/index.html create mode 100644 static/en/index.html create mode 100644 static/en/recipe-test/index.html rename style.css => static/en/style.css (95%) diff --git a/Cargo.lock b/Cargo.lock index 68f9d67..3eacc23 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,12 +8,42 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "build_html" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e121f1569e70b069135a0768a6b62a16c9c0eeaba92815a4baa6fdb93a97ec79" +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cc" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" +dependencies = [ + "jobserver", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -38,11 +68,66 @@ dependencies = [ name = "cooking" version = "0.1.0" dependencies = [ + "bincode", "build_html", "chrono", + "serde", + "serde_derive", + "sled", "uuid", ] +[[package]] +name = "crc32fast" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2209c310e29876f7f0b2721e7e26b84aff178aa3da5d091f9bfbf47669e60e3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97242a70df9b89a65d0b6df3c4bf5b9ce03c5b7309019777fbde37e7537f8762" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcae03edb34f947e64acdb1c33ec169824e20657e9ecb61cef6c8c74dcb8120" +dependencies = [ + "cfg-if", + "lazy_static", +] + +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + [[package]] name = "getrandom" version = "0.2.4" @@ -54,12 +139,63 @@ dependencies = [ "wasi", ] +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "jobserver" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +dependencies = [ + "libc", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.113" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eef78b64d87775463c549fbd80e19249ef436ea3bf1de2a1eb7e717ec7fab1e9" +[[package]] +name = "lock_api" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + [[package]] name = "num-integer" version = "0.1.44" @@ -79,12 +215,55 @@ dependencies = [ "autocfg", ] +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + [[package]] name = "ppv-lite86" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" +[[package]] +name = "proc-macro2" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" +dependencies = [ + "proc-macro2", +] + [[package]] name = "rand" version = "0.8.4" @@ -125,11 +304,74 @@ dependencies = [ "rand_core", ] +[[package]] +name = "redox_syscall" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +dependencies = [ + "bitflags", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + [[package]] name = "serde" version = "1.0.135" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2cf9235533494ea2ddcdb794665461814781c53f19d87b76e571a1c35acbad2b" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.135" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dcde03d87d4c973c04be249e7d8f0b35db1c848c487bd43032808e59dd8328d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sled" +version = "0.34.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f96b4737c2ce5987354855aed3797279def4ebf734436c6aa4552cf8e169935" +dependencies = [ + "crc32fast", + "crossbeam-epoch", + "crossbeam-utils", + "fs2", + "fxhash", + "libc", + "log", + "parking_lot", + "zstd", +] + +[[package]] +name = "smallvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" + +[[package]] +name = "syn" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] [[package]] name = "time" @@ -141,6 +383,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + [[package]] name = "uuid" version = "1.0.0-alpha.1" @@ -179,3 +427,32 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "zstd" +version = "0.9.2+zstd.1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2390ea1bf6c038c39674f22d95f0564725fc06034a47129179810b2fc58caa54" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "4.1.3+zstd.1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e99d81b99fb3c2c2c794e3fe56c305c63d5173a16a46b5850b07c935ffc7db79" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "1.6.2+zstd.1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2daf2f248d9ea44454bfcb2516534e8b8ad2fc91bf818a1885495fc42bc8ac9f" +dependencies = [ + "cc", + "libc", +] diff --git a/Cargo.toml b/Cargo.toml index 761e453..1c68e00 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,3 +19,7 @@ panic = "abort" build_html = "2.0.0" uuid = { version = "1.0.0-alpha.1", features = ["v4", "serde", "fast-rng"] } chrono = { version = "0.4", features = ["serde"] } +sled = { version = "0.34.7", features = ["compression"] } +bincode = "1.3.3" +serde = { version = "1.0", features = ["derive"] } +serde_derive = "1.0.132" diff --git a/index.html b/index.html deleted file mode 100644 index 359f747..0000000 --- a/index.html +++ /dev/null @@ -1 +0,0 @@ -Catgirl Cooking

🥘 Catgirl Cooking


The cutest cooking site on the net :3

Absolutely no ads, tracking, or nazis, ever.

Tags:

breakfast,apples,cheese,vegan,vegetarian,

Recipes:

\ No newline at end of file diff --git a/src/io.rs b/src/io.rs index 3d7aa3f..3ef2454 100644 --- a/src/io.rs +++ b/src/io.rs @@ -1,19 +1,148 @@ use std::fs::File; +use std::fs::OpenOptions; use std::io::prelude::*; -use std::path::Path; +use std::path::{PathBuf}; +use std::env; +use uuid::Uuid; -pub fn write_html(html: String) { - let path = Path::new("index.html"); +use crate::structures::*; + +pub fn write_html(html: String, lang: &str, shortcode: Option<&str>) { + // Create a path to write the file to + let mut path = PathBuf::new(); + path.push("static"); // base path for web content + path.push(lang); // not really that useful right now + match shortcode { // If there's a shortcode (for a recipe), use that path + Some(short) => { + path.push(short); + path.push("index.html"); + }, + None => path.push("index.html"), // Otherwise just write to the main index + } + + // If the directory doesn't exist, create it + let prefix = path.parent().unwrap(); + std::fs::create_dir_all(prefix).unwrap(); // Open file in write-only mode - let mut file = match File::create(&path) { + let mut file = match OpenOptions::new() + .write(true) + .create(true) + .open(&path) { Err(why) => panic!("couldn't create file: {}", why), Ok(file) => file, }; - // Write the HTML to fil + // Write the HTML to file match file.write_all(html.as_bytes()) { Err(why) => panic!("couldn't write to file: {}", why), Ok(_) => println!("successfully wrote to file"), } } + +pub fn open_database(keyspace: &str) -> Option { + let db: sled::Db; + let key = "HOME"; + let database_path = match env::var(key) { + Ok(val) => { + let mut tmp_path = PathBuf::new(); + tmp_path.push(val); + tmp_path.push(".local/share/catgirl-cooking"); + tmp_path.push("database"); + tmp_path + }, + Err(e) => { + panic!("failed to get key: {}", e); + } + }; + + match sled::open(database_path) { + Ok(database) => db = database, + Err(error) => { + println!("error in opening database: {}", error); + return None; + } + } + + let database: Option = match db.open_tree(keyspace) { + Ok(tree) => Some(tree), + Err(e) => { println!("error in opening Tree: {}", e); None }, + }; + return database; +} + +pub fn db_insert_recipe(recipe: Recipe) { + let db = match open_database("recipes") { + Some(database) => database, + None => panic!(), + }; + + let bytes; + match bincode::serialize(&recipe) { + Ok(result) => bytes = result, + Err(error) => panic!("could not serialize recipe: {}", error), + } + + match db.insert(recipe.id.as_simple().to_string(), bytes) { + Ok(_) => println!("inserted recipe"), + Err(error) => panic!("failed to insert recipe: {}", error), + } + + db.flush().unwrap(); +} + +pub fn db_get_recipe(id: Uuid) -> Recipe { + let db = match open_database("recipes") { + Some(database) => database, + None => panic!(), + }; + + let db_entry = match db.get(id.as_simple().to_string()) { + Ok(entry) => entry, + Err(error) => panic!("failed to get recipe: {}", error), + }; + + if let Some(entry) = db_entry { + let read_entry = match bincode::deserialize(&entry) { + Ok(entry) => entry, + Err(error) => panic!("failed to deserialize entry: {}", error), + }; + return read_entry; + } else { + panic!("failed to find entry"); + } +} + +pub fn db_get_all_recipes() -> Vec { + let db = match open_database("recipes") { + Some(database) => database, + None => panic!(), + }; + + let first_key = match db.first() { + Ok(pair) => match pair { + Some(key) => key.0, + None => panic!("error getting first key"), + } + Err(error) => panic!("error getting first key: {}", error), + }; + + let mut recipes_list: Vec = Vec::new(); + let mut iter = db.range(first_key..); + + loop { + match iter.next() { + Some(x) => { + let read_entry; + match bincode::deserialize(&x.unwrap().1) { + Ok(result) => read_entry = result, + Err(error) => panic!("failed to deserialize entry: {}", error), + } + recipes_list.push(read_entry); + }, + None => break, + } + } + + return recipes_list; +} diff --git a/src/main.rs b/src/main.rs index e03c927..dd17e6f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,14 @@ use build_html::{self, Html, HtmlContainer, ContainerType, Container}; +use uuid::Uuid; mod structures; mod io; mod tests; -fn main() { +use crate::structures::*; +use crate::structures::{MeasurementUnits::*, TimeUnits::*}; + +fn construct_main_page() { // Specify info to go onto the base page let base_page = build_html::HtmlPage::new() .with_head_link("favicon.ico", "icon") // Favicon @@ -32,12 +36,11 @@ fn main() { // Create (test) lists of tags & recipes let tags: Vec<&str> = vec!["breakfast", "apples", "cheese", "vegan", "vegetarian"]; - let recipes: Vec<&str> = vec!["Macaroni & Cheese", "Soup", "Apple Pie", "More Soup", "idk", "cum"]; + let recipes = io::db_get_all_recipes(); home_page.add_raw(r#""#); // Tags in italics - home_page.add_paragraph("Tags:"); let mut tags_html = String::new(); // Create custom html string for tags - tags_html.push_str(r#"

"#); // Begin paragraph element + tags_html.push_str(r#"

Tags: "#); // Begin paragraph element for x in tags { // Go through the list of tags tags_html.push_str(format!("{},", x, x).as_str()); // Append the tags as a link } @@ -49,11 +52,36 @@ fn main() { home_page.add_header(2, "Recipes:"); let mut recipes_container = Container::new(ContainerType::UnorderedList); for i in recipes { - recipes_container.add_link(i, i); + let mut link = String::new(); + link.push_str("/"); + link.push_str(&i.shortcode); + recipes_container.add_link(link, &i.name); } home_page.add_container(recipes_container); home_page.add_container(footer); - io::write_html(home_page.to_html_string()); + // Create the main page file + io::write_html(home_page.to_html_string(), "en", None); +} + +fn rebuild() { + construct_main_page(); + for i in io::db_get_all_recipes() { + i.construct_page(); + } +} + +fn main() { + /* + let full_recipe = Recipe::new( + "Full Recipe".to_string(), + (3, Minutes), + (4, Hours), + 7, + vec![(3, Cups, "flour".to_string()), (4, Tablespoons, "sugar".to_string()), (1, Cups, "water".to_string())], + vec!["Mix flour and sugar in a bowl".to_string(), "Slowly add water".to_string(), "Bake in the oven for 4 hours".to_string()], + "Erin Nova".to_string(), + Some(vec![Tag('#', "meow".to_string()), Tag('#', "gay".to_string())]));*/ + rebuild(); } diff --git a/src/structures.rs b/src/structures.rs index 68c7908..4e5c3dc 100644 --- a/src/structures.rs +++ b/src/structures.rs @@ -1,5 +1,9 @@ use uuid::Uuid; use chrono::prelude::*; +use serde_derive::{Deserialize, Serialize}; +use build_html::{self, Html, HtmlContainer, ContainerType, Container, HtmlPage}; + +use crate::io::write_html; // Content strings to add to html pub const TITLE: &str = r#"Catgirl Cooking"#; @@ -7,12 +11,14 @@ pub const HEADER: &str = r#"🥘 Catgirl Cooking"#; pub const DESCRIPTION: &str = r#"The cutest cooking site on the net :3"#; pub const DETAILS: &str = r#"Absolutely no ads, tracking, or nazis, ever."#; +#[derive(Debug, Serialize, Deserialize)] pub enum TimeUnits { Hours, - Min, - Sec, + Minutes, + Seconds, } +#[derive(Debug, Serialize, Deserialize)] pub enum MeasurementUnits { Grams, Kilograms, @@ -21,13 +27,14 @@ pub enum MeasurementUnits { Millilitres, Gallons, Ounces, - Pinch, - Drop, - Tablespoon, - Teaspoon, + Pinches, + Drops, + Cups, + Tablespoons, + Teaspoons, } -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] pub struct Tag(pub char, pub String); impl Tag { @@ -55,16 +62,140 @@ impl Tag { } } +#[derive(Debug, Serialize, Deserialize)] pub struct Recipe { - id: Uuid, // Unique recipe ID - name: String, // Full recipe name - prep_time: (i32, TimeUnits), // Preparation time: (value, units) - cooking_time: (i32, TimeUnits), // Cooking time: (value, units) - servings: u8, // How many servinge the recipe makes - ingredients: Vec<(i32, MeasurementUnits, String)>, // Vector of ingredients: (value, units, name) - directions: Vec<(u16, String)>, // List of instructions: (step #, details) - attribution: String, // Author name - posted_date: DateTime, // Date the recipe was first posted - edited_date: DateTime, // Date the recipe was last edited - tags: Vec, // List of tags + pub id: Uuid, // Unique recipe ID + pub name: String, // Full recipe name + pub shortcode: String, + pub prep_time: (i32, TimeUnits), // Preparation time: (value, units) + pub cooking_time: (i32, TimeUnits), // Cooking time: (value, units) + pub servings: u8, // How many servinge the recipe makes + pub ingredients: Vec<(i32, MeasurementUnits, String)>, // Vector of ingredients: (value, units, name) + pub directions: Vec<(u16, String)>, // List of instructions: (step #, details) + pub attribution: String, // Author name + pub posted_date: DateTime, // Date the recipe was first posted + pub edited_date: Option>, // Date the recipe was last edited + pub tags: Option>, // List of tags +} + +impl Recipe { + pub fn example(name: &str, time: i32, directions: Vec<&str>, ingredients: Vec<&str>) -> Recipe { + let mut ingredients_list: Vec<(i32, MeasurementUnits, String)> = Vec::new(); + for i in 0..ingredients.len() { + ingredients_list.push((i.try_into().unwrap(), MeasurementUnits::Litres, ingredients[i].to_string())); + } + + let mut directions_list: Vec<(u16, String)> = Vec::new(); + for n in 0..directions.len() { + directions_list.push((n.try_into().unwrap(), directions[n].to_string())); + } + + Recipe { + id: Uuid::new_v4(), + name: name.to_string(), + shortcode: construct_shortcode(name.to_string()), + prep_time: (time, TimeUnits::Minutes), + cooking_time: (time, TimeUnits::Hours), + servings: 4, + ingredients: ingredients_list, + directions: directions_list, + attribution: "Erin".to_string(), + posted_date: Utc::now(), + edited_date: None, + tags: None, + } + } + + pub fn new(name: String, prep_time: (i32, TimeUnits), cooking_time: (i32, TimeUnits), servings: u8, ingredients: Vec<(i32, MeasurementUnits, String)>, directions: Vec, attribution: String, tags: Option>) -> Recipe { + let mut directions_list: Vec<(u16, String)> = Vec::new(); + for i in 0..directions.len() { + directions_list.push((i.try_into().unwrap(), directions[i].to_string())); + } + + Recipe { + id: Uuid::new_v4(), + name: name.clone(), + shortcode: construct_shortcode(name.clone()), + prep_time, + cooking_time, + servings, + ingredients, + directions: directions_list, + attribution, + posted_date: Utc::now(), + edited_date: None, + tags, + } + } + + pub fn construct_page(&self) { + let mut recipe_page = build_html::HtmlPage::new() + .with_head_link("/favicon.ico", "icon") // Favicon + .with_stylesheet("/style.css") // Link stylesheet + .with_meta(vec![("charset", "UTF-8")]) + .with_meta(vec![("name", "viewport"), ("content", "width=device-width, initial-scale=1")]) // Display stuff + .with_meta(vec![("name", "description"), ("content", DESCRIPTION)]) // Add the description + .with_title(TITLE) + .with_header(1, &self.name) + .with_raw(r#"


"#); + + // Metadata + let meta = Container::new(ContainerType::UnorderedList) + .with_attributes(vec![("class", "recipe")]) + .with_paragraph(format!("⏲️ Preparation time: {} {:?}", &self.prep_time.0, &self.prep_time.1)) + .with_paragraph(format!("🍳 Cooking time: {} {:?}", &self.cooking_time.0, &self.cooking_time.1)) + .with_paragraph(format!("🍽️ Servings: {}", &self.servings)); + recipe_page.add_container(meta); + + // Append ingredients + recipe_page.add_header(2, r#"Ingredients:"#); + let mut ingredients_container = Container::new(ContainerType::UnorderedList) + .with_attributes(vec![("class", "recipe")]); + for i in &self.ingredients { + let ingredient = format!("{:?} {:?} {}", &i.0, &i.1, &i.2); + ingredients_container.add_paragraph(ingredient); + } + recipe_page.add_container(ingredients_container); + + // Append directions + recipe_page.add_header(2, r#"Directions:"#); + let mut directions_container = Container::new(ContainerType::OrderedList) + .with_attributes(vec![("class", "recipe")]); + for n in &self.directions { + directions_container.add_paragraph(&n.1); + } + recipe_page.add_container(directions_container); + + recipe_page.add_header(2, r#"Author:"#); + recipe_page.add_paragraph(&self.attribution); + + // Append tags + let mut tags_html = String::new(); + tags_html.push_str(r#"

Tags: "#); + match &self.tags { + Some(list) => { + for x in list { + tags_html.push_str(format!("#{},", x.1, x.1).as_str()); + } + }, + None => { + tags_html.push_str(r#"no tags!! :o"#); + }, + } + + + write_html(recipe_page.to_html_string(), "en", Some(&self.shortcode)); + } +} + +pub fn construct_shortcode(full_name: String) -> String { + let mut prev_name = full_name; + prev_name.make_ascii_lowercase(); + return prev_name + .chars() + .map(|x| match x { + ' ' => '-', + _ => x, + }) + .collect(); } diff --git a/src/tests.rs b/src/tests.rs index 00b93c4..49422a4 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -17,4 +17,9 @@ mod tests { fn test_tag_to_string() { assert_eq!(Tag('#', "test".to_string()).to_string(), "#test".to_string()); } + + #[test] + fn test_shortcode_conversion() { + assert_eq!(construct_shortcode("Test SHORtcode".to_string()), "test-shortcode".to_string()); + } } diff --git a/static/en/another-one!/index.html b/static/en/another-one!/index.html new file mode 100644 index 0000000..2cade29 --- /dev/null +++ b/static/en/another-one!/index.html @@ -0,0 +1 @@ +Catgirl Cooking

Another One!


  • ⏲️ Preparation time: 5 Minutes

  • 🍳 Cooking time: 5 Hours

  • 🍽️ Servings: 4

Ingredients:

  • 0 Litres flour

  • 1 Litres eggs

  • 2 Litres sugar

Directions:

  1. prepare ingredients

  2. cook it

Author:

Erin

\ No newline at end of file diff --git a/static/en/full-recipe/index.html b/static/en/full-recipe/index.html new file mode 100644 index 0000000..2c33684 --- /dev/null +++ b/static/en/full-recipe/index.html @@ -0,0 +1 @@ +Catgirl Cooking

Full Recipe


  • ⏲️ Preparation time: 3 Minutes

  • 🍳 Cooking time: 4 Hours

  • 🍽️ Servings: 7

Ingredients:

  • 3 Cups flour

  • 4 Tablespoons sugar

  • 1 Cups water

Directions:

  1. Mix flour and sugar in a bowl

  2. Slowly add water

  3. Bake in the oven for 4 hours

Author:

Erin Nova

\ No newline at end of file diff --git a/static/en/index.html b/static/en/index.html new file mode 100644 index 0000000..cb25b77 --- /dev/null +++ b/static/en/index.html @@ -0,0 +1 @@ +Catgirl Cooking

🥘 Catgirl Cooking


The cutest cooking site on the net :3

Absolutely no ads, tracking, or nazis, ever.

Tags: breakfast,apples,cheese,vegan,vegetarian,

Recipes:


homeatom

Software licensed under the CNPLv7+

Recipes under Public Domain

\ No newline at end of file diff --git a/static/en/recipe-test/index.html b/static/en/recipe-test/index.html new file mode 100644 index 0000000..3a40cf2 --- /dev/null +++ b/static/en/recipe-test/index.html @@ -0,0 +1 @@ +Catgirl Cooking

Recipe Test


  • ⏲️ Preparation time: 3 Minutes

  • 🍳 Cooking time: 3 Hours

  • 🍽️ Servings: 4

Ingredients:

  • 0 Litres flour

  • 1 Litres sugar

  • 2 Litres nothing

Directions:

  1. prepare ingredients

  2. cook it

  3. meow

Author:

Erin

\ No newline at end of file diff --git a/style.css b/static/en/style.css similarity index 95% rename from style.css rename to static/en/style.css index c20a1b0..7a7b25e 100644 --- a/style.css +++ b/static/en/style.css @@ -26,7 +26,6 @@ body { max-width: 800px; margin: auto; padding: 0 16px; - margin-bottom: 500px; font-family: 'Atkinson Hyperlegible', sans-serif; } @@ -57,3 +56,7 @@ img { ul { column-count: 3; } + +ul.recipe { + column-count: 1; +}