catgirl-cooking/src/structures.rs

202 lines
6.8 KiB
Rust
Raw Normal View History

2022-01-23 18:40:13 +00:00
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;
2022-01-23 18:40:13 +00:00
// Content strings to add to html
pub const TITLE: &str = r#"Catgirl Cooking"#;
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)]
2022-01-23 18:40:13 +00:00
pub enum TimeUnits {
Hours,
Minutes,
Seconds,
2022-01-23 18:40:13 +00:00
}
#[derive(Debug, Serialize, Deserialize)]
2022-01-23 18:40:13 +00:00
pub enum MeasurementUnits {
Grams,
Kilograms,
Pounds,
Litres,
Millilitres,
Gallons,
Ounces,
Pinches,
Drops,
Cups,
Tablespoons,
Teaspoons,
2022-01-23 18:40:13 +00:00
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
2022-01-23 18:40:13 +00:00
pub struct Tag(pub char, pub String);
impl Tag {
pub fn new(s: String) -> Tag {
Tag('#', s)
}
pub fn to_string(&self) -> String {
let mut tag = String::new();
tag.push_str(&self.0.to_string());
tag.push_str(&self.1);
return tag;
}
pub fn from_string(s: String) -> Tag {
let hash = s.chars().nth(0).unwrap();
let mut text = s;
text.remove(0);
text.make_ascii_lowercase();
return Tag(hash, text);
}
pub fn display(&self) {
println!("{}{}", self.0, self.1);
}
}
#[derive(Debug, Serialize, Deserialize)]
2022-01-23 18:40:13 +00:00
pub struct Recipe {
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();
2022-01-23 18:40:13 +00:00
}