Add database functionality, recipe pages
This commit is contained in:
parent
b098b6f708
commit
db1dcc0704
|
@ -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",
|
||||
]
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
<!DOCTYPE html><html><head><link href="favicon.ico" rel="icon"><link href="style.css" rel="stylesheet"><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="description" content="The cutest cooking site on the net :3"><title>Catgirl Cooking</title></head><body><h1>🥘 Catgirl Cooking</h1><hr><p>The cutest cooking site on the net :3</p><p>Absolutely no ads, tracking, or nazis, ever.</p><i><p>Tags:</p><p><a href='breakfast'>breakfast</a>,<a href='apples'>apples</a>,<a href='cheese'>cheese</a>,<a href='vegan'>vegan</a>,<a href='vegetarian'>vegetarian</a>,</p></i><h2>Recipes:</h2><ul><li><a href="Macaroni & Cheese">Macaroni & Cheese</a></li><li><a href="Soup">Soup</a></li><li><a href="Apple Pie">Apple Pie</a></li><li><a href="More Soup">More Soup</a></li><li><a href="idk">idk</a></li><li><a href="cum">cum</a></li></ul><footer><hr><a href="/">home</a><a href="/atom.xml">atom</a><p>Software licensed under the CNPLv7+</p><p>Recipes under Public Domain</p></footer></body></html>
|
139
src/io.rs
139
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<sled::Tree> {
|
||||
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<sled::Tree> = 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<Recipe> {
|
||||
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<Recipe> = 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;
|
||||
}
|
||||
|
|
40
src/main.rs
40
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#"<i>"#); // 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#"<p>"#); // Begin paragraph element
|
||||
tags_html.push_str(r#"<p>Tags: "#); // Begin paragraph element
|
||||
for x in tags { // Go through the list of tags
|
||||
tags_html.push_str(format!("<a href='{}'>{}</a>,", 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();
|
||||
}
|
||||
|
|
|
@ -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<Utc>, // Date the recipe was first posted
|
||||
edited_date: DateTime<Utc>, // Date the recipe was last edited
|
||||
tags: Vec<Tag>, // 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<Utc>, // Date the recipe was first posted
|
||||
pub edited_date: Option<DateTime<Utc>>, // Date the recipe was last edited
|
||||
pub tags: Option<Vec<Tag>>, // 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<String>, attribution: String, tags: Option<Vec<Tag>>) -> 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#"<hr>"#);
|
||||
|
||||
// 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#"<i><p>Tags: "#);
|
||||
match &self.tags {
|
||||
Some(list) => {
|
||||
for x in list {
|
||||
tags_html.push_str(format!("<a href='/tags/{}'>#{}</a>,", 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();
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<!DOCTYPE html><html><head><link href="/favicon.ico" rel="icon"><link href="/style.css" rel="stylesheet"><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="description" content="The cutest cooking site on the net :3"><title>Catgirl Cooking</title></head><body><h1>Another One!</h1><hr><ul class="recipe"><li><p>⏲️ Preparation time: 5 Minutes</p></li><li><p>🍳 Cooking time: 5 Hours</p></li><li><p>🍽️ Servings: 4</p></li></ul><h2>Ingredients:</h2><ul class="recipe"><li><p>0 Litres flour</p></li><li><p>1 Litres eggs</p></li><li><p>2 Litres sugar</p></li></ul><h2>Directions:</h2><ol class="recipe"><li><p>prepare ingredients</p></li><li><p>cook it</p></li></ol><h2>Author:</h2><p>Erin</p></body></html>
|
|
@ -0,0 +1 @@
|
|||
<!DOCTYPE html><html><head><link href="/favicon.ico" rel="icon"><link href="/style.css" rel="stylesheet"><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="description" content="The cutest cooking site on the net :3"><title>Catgirl Cooking</title></head><body><h1>Full Recipe</h1><hr><ul class="recipe"><li><p>⏲️ Preparation time: 3 Minutes</p></li><li><p>🍳 Cooking time: 4 Hours</p></li><li><p>🍽️ Servings: 7</p></li></ul><h2>Ingredients:</h2><ul class="recipe"><li><p>3 Cups flour</p></li><li><p>4 Tablespoons sugar</p></li><li><p>1 Cups water</p></li></ul><h2>Directions:</h2><ol class="recipe"><li><p>Mix flour and sugar in a bowl</p></li><li><p>Slowly add water</p></li><li><p>Bake in the oven for 4 hours</p></li></ol><h2>Author:</h2><p>Erin Nova</p></body></html>
|
|
@ -0,0 +1 @@
|
|||
<!DOCTYPE html><html><head><link href="favicon.ico" rel="icon"><link href="style.css" rel="stylesheet"><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="description" content="The cutest cooking site on the net :3"><title>Catgirl Cooking</title></head><body><h1>🥘 Catgirl Cooking</h1><hr><p>The cutest cooking site on the net :3</p><p>Absolutely no ads, tracking, or nazis, ever.</p><i><p>Tags: <a href='breakfast'>breakfast</a>,<a href='apples'>apples</a>,<a href='cheese'>cheese</a>,<a href='vegan'>vegan</a>,<a href='vegetarian'>vegetarian</a>,</p></i><h2>Recipes:</h2><ul><li><a href="/recipe-test">Recipe Test</a></li><li><a href="/recipe-test">Recipe Test</a></li><li><a href="/full-recipe">Full Recipe</a></li><li><a href="/another-one!">Another One!</a></li></ul><footer><hr><a href="/">home</a><a href="/atom.xml">atom</a><p>Software licensed under the CNPLv7+</p><p>Recipes under Public Domain</p></footer></body></html>
|
|
@ -0,0 +1 @@
|
|||
<!DOCTYPE html><html><head><link href="/favicon.ico" rel="icon"><link href="/style.css" rel="stylesheet"><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="description" content="The cutest cooking site on the net :3"><title>Catgirl Cooking</title></head><body><h1>Recipe Test</h1><hr><ul class="recipe"><li><p>⏲️ Preparation time: 3 Minutes</p></li><li><p>🍳 Cooking time: 3 Hours</p></li><li><p>🍽️ Servings: 4</p></li></ul><h2>Ingredients:</h2><ul class="recipe"><li><p>0 Litres flour</p></li><li><p>1 Litres sugar</p></li><li><p>2 Litres nothing</p></li></ul><h2>Directions:</h2><ol class="recipe"><li><p>prepare ingredients</p></li><li><p>cook it</p></li><li><p>meow</p></li></ol><h2>Author:</h2><p>Erin</p></body></html>
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue