pub mod add;
pub mod behold;
pub mod errors;
pub mod plan;
pub mod utils;
pub mod view;
use crate::errors::{IngredientParsingError, RecipeParsingError};
use colored::*;
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::fs;
use std::path::PathBuf;
use std::str::FromStr;
use tabled::Tabled;
const UNITS: [&str; 10] = [
"can", "cup", "gallon", "gram", "item", "kg", "lb", "oz", "tsp", "tbsp",
];
pub const WEEK: [&str; 7] = [
"Sunday",
"Monday",
"Tuedsay",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
];
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Recipe {
name: String,
tags: Vec<String>,
ingredients: Vec<Ingredient>,
steps: Vec<String>,
}
impl Recipe {
pub fn to_row(self, id: usize) -> RecipeRow {
RecipeRow {
ID: id,
Name: self.name,
Tags: self.tags.join(", "),
}
}
pub fn summary(&self) -> String {
format!(
"{:30} -- {}",
self.name.replace("-", " "),
self.tags.join(", ")
)
}
}
impl Display for Recipe {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
let name = self.name.purple();
let tags = self.tags.join(", ").green();
let ingredients = self
.ingredients
.iter()
.map(|x| x.to_string())
.collect::<Vec<String>>()
.join("\n⇒ ")
.blue();
let steps = self
.steps
.iter()
.map(|x| x.to_string())
.collect::<Vec<String>>()
.join("\n🡢 ")
.white();
write!(
f,
"{name}\n\t{} {tags}\n\n{} {ingredients}\n\n🡢 {steps}\n",
"→ Tags:".green(),
"⇒".blue()
)
}
}
impl TryFrom<&PathBuf> for Recipe {
type Error = RecipeParsingError;
fn try_from(path: &PathBuf) -> Result<Self, Self::Error> {
if !path.exists() {
panic!("The file does not exist dumbass: {path:?}")
}
Ok(serde_yaml::from_str(&fs::read_to_string(&path)?)?)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Ingredient {
name: String,
amount: f32,
unit: Unit,
}
impl Display for Ingredient {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
write!(f, "{} {} {}", self.amount, self.unit, self.name)
}
}
impl FromStr for Ingredient {
type Err = IngredientParsingError;
fn from_str(input: &str) -> Result<Ingredient, Self::Err> {
let split = input.split(" ").collect::<Vec<_>>();
let amount = split[0].parse::<f32>()?;
let unit = split[1].parse::<Unit>()?;
let name = split[2..].join(" ");
Ok(Ingredient { name, amount, unit })
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Unit {
Can,
Cup,
Gallon,
Gram,
Item,
Kg,
Lb,
Oz,
Tbsp,
Tsp,
}
impl Display for Unit {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
match *self {
Unit::Can => write!(f, "Can"),
Unit::Cup => write!(f, "Cup"),
Unit::Gallon => write!(f, "Gallon"),
Unit::Gram => write!(f, "Gram"),
Unit::Item => write!(f, "Item"),
Unit::Kg => write!(f, "Kg"),
Unit::Lb => write!(f, "Lb"),
Unit::Oz => write!(f, "Oz"),
Unit::Tsp => write!(f, "Tsp"),
Unit::Tbsp => write!(f, "Tbsp"),
}
}
}
impl FromStr for Unit {
type Err = IngredientParsingError;
fn from_str(input: &str) -> Result<Unit, Self::Err> {
match input.to_lowercase().as_str() {
"can" => Ok(Unit::Can),
"cup" => Ok(Unit::Cup),
"gallon" => Ok(Unit::Gallon),
"gram" => Ok(Unit::Gram),
"item" => Ok(Unit::Item),
"kg" => Ok(Unit::Kg),
"lb" => Ok(Unit::Lb),
"oz" => Ok(Unit::Oz),
"tsp" => Ok(Unit::Tsp),
"tbsp" => Ok(Unit::Tbsp),
_ => Err(IngredientParsingError::InvalidUnit(input.into())),
}
}
}
#[allow(non_snake_case)]
#[derive(Tabled)]
pub struct RecipeRow {
ID: usize,
pub Name: String,
Tags: String,
}
#[allow(non_snake_case)]
#[derive(Tabled)]
pub struct IngredientRow {
pub Name: String,
pub Amount: String,
pub Unit: String,
}
impl TryFrom<Ingredient> for IngredientRow {
type Error = IngredientParsingError;
fn try_from(ingr: Ingredient) -> Result<Self, Self::Error> {
Ok(IngredientRow {
Name: ingr.name.clone(),
Amount: ingr.amount.to_string(),
Unit: ingr.unit.to_string(),
})
}
}
#[allow(non_snake_case)]
#[derive(Tabled)]
pub struct StepRow {
pub Step: u16,
pub Details: String,
}
#[allow(non_snake_case)]
#[derive(Tabled)]
pub struct PlanRow {
Date: String,
Sunday: String,
Monday: String,
Tuesday: String,
Wednesday: String,
Thursday: String,
Friday: String,
Saturday: String,
}
#[allow(non_snake_case)]
#[derive(Debug, Tabled)]
pub struct GroceryRow {
Id: usize,
Amount: f32,
Unit: Unit,
Ingredient: String,
}