183 lines
4.8 KiB
Rust
183 lines
4.8 KiB
Rust
use super::schema::recipes;
|
|
use super::schema::ingredients;
|
|
use super::diesel::prelude::*;
|
|
|
|
pub mod fields {
|
|
use diesel::{
|
|
backend::Backend,
|
|
sql_types::*,
|
|
deserialize::{self, FromSql},
|
|
serialize::{self, Output, ToSql},
|
|
};
|
|
use std::io::Write;
|
|
|
|
/// All recipes have a single associated category
|
|
/// representing the main use of the resulting preparation.
|
|
///
|
|
/// It is stored as Integer
|
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, FromSqlRow, AsExpression)]
|
|
#[sql_type = "SmallInt"]
|
|
pub enum RecipeCategory {
|
|
Breakfast = 0,
|
|
Starter = 1,
|
|
MainCourse = 2,
|
|
Dessert = 3
|
|
}
|
|
|
|
impl RecipeCategory {
|
|
pub fn id(&self) -> i16 {
|
|
*self as i16
|
|
}
|
|
|
|
pub fn name(&self) -> &str {
|
|
match *self {
|
|
RecipeCategory::Breakfast => "Petit-déjeuner",
|
|
RecipeCategory::Starter => "Entrée",
|
|
RecipeCategory::MainCourse => "Plat principal",
|
|
RecipeCategory::Dessert => "Dessert"
|
|
}
|
|
}
|
|
|
|
pub fn all() -> [Self; 4] {
|
|
[RecipeCategory::Breakfast,
|
|
RecipeCategory::Starter,
|
|
RecipeCategory::MainCourse,
|
|
RecipeCategory::Dessert]
|
|
}
|
|
|
|
pub fn from_id(id: i16) -> Option<RecipeCategory> {
|
|
match id {
|
|
0 => Some(RecipeCategory::Breakfast),
|
|
1 => Some(RecipeCategory::Starter),
|
|
2 => Some(RecipeCategory::MainCourse),
|
|
3 => Some(RecipeCategory::Dessert),
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<DB: Backend> FromSql<SmallInt, DB> for RecipeCategory
|
|
where i16: FromSql<SmallInt, DB>
|
|
{
|
|
fn from_sql(bytes: Option<&DB::RawValue>) -> deserialize::Result<Self> {
|
|
let v = i16::from_sql(bytes)?;
|
|
if let Some(result) = RecipeCategory::from_id(v){ Ok(result) }
|
|
else { Err("Invalid RecipeCategory id".into()) }
|
|
}
|
|
}
|
|
|
|
impl<DB: Backend> ToSql<SmallInt, DB> for RecipeCategory
|
|
where i16: ToSql<SmallInt, DB>
|
|
{
|
|
fn to_sql<W: Write>(&self, out: &mut Output<W, DB>) -> serialize::Result {
|
|
i16::to_sql(&(*self as i16), out)
|
|
}
|
|
}
|
|
|
|
pub type IngredientId = i32;
|
|
|
|
#[derive(Debug, Clone, FromSqlRow, AsExpression)]
|
|
#[sql_type = "Text"]
|
|
pub struct IngredientList(Vec<IngredientId>);
|
|
|
|
/// Just a basic method for now
|
|
impl IngredientList {
|
|
pub fn new() -> Self {
|
|
IngredientList(Vec::new())
|
|
}
|
|
|
|
pub fn as_string(&self) -> String {
|
|
self.0.iter()
|
|
.map(|i| format!("{}", i))
|
|
.collect::<Vec<String>>()
|
|
.join(" ")
|
|
}
|
|
}
|
|
|
|
impl std::ops::Deref for IngredientList {
|
|
type Target = Vec<IngredientId>;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.0
|
|
}
|
|
}
|
|
|
|
impl std::ops::DerefMut for IngredientList {
|
|
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
&mut self.0
|
|
}
|
|
}
|
|
|
|
impl<DB: Backend> FromSql<Text, DB> for IngredientList
|
|
where String: FromSql<Text, DB>
|
|
{
|
|
fn from_sql(bytes: Option<&DB::RawValue>) -> deserialize::Result<Self> {
|
|
let data = String::from_sql(bytes)?;
|
|
Ok(
|
|
IngredientList(
|
|
data.split(" ")
|
|
.map(|i| i.parse::<IngredientId>().unwrap())
|
|
.collect()
|
|
))
|
|
}
|
|
}
|
|
|
|
impl<DB: Backend> ToSql<Text, DB> for IngredientList
|
|
where String: ToSql<Text, DB>
|
|
{
|
|
fn to_sql<W: Write>(&self, out: &mut Output<W, DB>) -> serialize::Result {
|
|
String::to_sql(&self.as_string(), out)
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/// Data for a recipe stored in DB
|
|
#[derive(Debug, Clone, Queryable)]
|
|
pub struct Recipe {
|
|
pub id: i32,
|
|
pub title: String,
|
|
pub category: fields::RecipeCategory,
|
|
pub ingredients: fields::IngredientList,
|
|
pub preparation: String,
|
|
}
|
|
|
|
impl PartialEq for Recipe {
|
|
fn eq(&self, other: &Recipe) -> bool {
|
|
self.id == other.id
|
|
}
|
|
}
|
|
|
|
#[derive(Insertable, Debug)]
|
|
#[table_name="recipes"]
|
|
pub struct NewRecipe<'a> {
|
|
pub title: &'a str,
|
|
pub category: fields::RecipeCategory,
|
|
pub ingredients: fields::IngredientList,
|
|
pub preparation: &'a str,
|
|
}
|
|
|
|
impl<'a> NewRecipe<'a> {
|
|
pub fn insert(self, conn: &SqliteConnection) -> Result<Self, String> {
|
|
diesel::insert_into(recipes::table)
|
|
.values(&self)
|
|
.execute(conn)
|
|
.expect("Error inserting recipe");
|
|
Ok(self)
|
|
}
|
|
}
|
|
|
|
#[derive(Queryable)]
|
|
pub struct Ingredient {
|
|
pub id: i32,
|
|
pub alias: String,
|
|
}
|
|
|
|
#[derive(Insertable)]
|
|
#[table_name="ingredients"]
|
|
pub struct NewIngredient<'a> {
|
|
pub alias: &'a str,
|
|
}
|