Works on ingredients

This commit is contained in:
2019-02-03 15:17:43 +01:00
parent 28afe8ece0
commit d3259c82b3
10 changed files with 151 additions and 61 deletions

View File

@@ -3,7 +3,22 @@ extern crate diesel;
use self::cookbook::*;
use self::models::*;
use self::diesel::prelude::*;
use diesel::sqlite::SqliteConnection;
use cookbook::models::fields::IngredientList;
struct IngredientListManager<'a>(&'a SqliteConnection, IngredientList);
impl<'a> IngredientListManager<'a> {
fn display_lines(&self) -> String {
let mut repr = String::new();
for id in self.1.iter() {
let ingdt = ingredients::get(self.0, id).expect("Database integrity error");
repr.push_str(&format!("{}\n", ingdt.alias));
}
repr
}
}
fn main() {
@@ -13,6 +28,6 @@ fn main() {
for rec in result {
println!("*************\n{}\n({:?})", rec.title, rec.category);
println!("-------------\n");
println!("{}", rec.ingredients);
println!("{}", rec.ingredients.as_string());
}
}

View File

@@ -1,16 +1,16 @@
extern crate cookbook;
extern crate diesel;
use std::io::{Read, stdin};
use std::io::{stdin};
use diesel::SqliteConnection;
use self::cookbook::*;
use self::models::{NewRecipe, fields::RecipeCategory};
use self::models::{NewRecipe, fields::RecipeCategory, fields::IngredientList};
struct CreateRecipe<'a> {
connection: &'a SqliteConnection,
title: &'a str,
category: Option<RecipeCategory>,
ingredients: String,
ingredients: IngredientList,
}
impl<'a> CreateRecipe<'a> {
@@ -19,7 +19,7 @@ impl<'a> CreateRecipe<'a> {
connection: conn,
title: "New recipe",
category: None,
ingredients: String::new(),
ingredients: IngredientList::new(),
}
}
@@ -35,23 +35,23 @@ impl<'a> CreateRecipe<'a> {
use crate::ingredients::*;
// Check it exists or create
if let Some(_ingdt) = find(self.connection, &name) {
println!("=");
} else {
create(self.connection, &name);
println!("+{}", &name);
match get_id_or_create(self.connection, &name) {
Ok(id) => {
println!("Got id {}", id);
self.ingredients.push(id);
},
Err(_) => println!("Error adding ingredient")
}
self.ingredients.push_str(&name);
}
/// Builds a NewRecipe instance from current data and insert it.
fn insert(self) {
let new_recipe = NewRecipe::new(
self.title,
self.category.unwrap_or(RecipeCategory::Breakfast),
&self.ingredients,
"");
let new_recipe = NewRecipe {
title: self.title,
category: self.category.unwrap_or(RecipeCategory::Breakfast),
ingredients: self.ingredients,
preparation: ""
};
match new_recipe.insert(self.connection) {
Ok(new) => println!("Added {}", new.title),
Err(e) => println!("Error: {}", e),

View File

@@ -40,34 +40,52 @@ pub mod recipes {
}
}
pub mod ingredients {
use crate::models::{Ingredient, NewIngredient};
use crate::models::{Ingredient, NewIngredient, fields::IngredientId};
use super::{SqliteConnection, schema};
use super::diesel::prelude::*;
pub fn find(conn: &SqliteConnection, name: &str) -> Option<Ingredient> {
use self::schema::ingredients::dsl::*;
let results = ingredients.filter(alias.like(name))
.limit(1)
.load::<Ingredient>(conn)
.expect("Error finding ingredient");
if !results.is_empty() {
Some(results.into_iter().nth(0).unwrap())
/// Returns the id of the ingredient identifiable by `name: &str`
/// If the ingredient does not yet exists, it is inserted in database.
pub fn get_id_or_create(conn: &SqliteConnection, name: &str) -> Result<i32,String> {
if let Some(ingdt) = find(conn, name) {
return Ok(ingdt.id);
} else {
None
return create(conn, name);
}
}
pub fn create(conn: &SqliteConnection, name: &str) -> usize {
use self::schema::ingredients;
pub fn get(conn: &SqliteConnection, ingdt_id: &IngredientId) -> Result<Ingredient,String> {
use self::schema::ingredients::dsl::*;
diesel::insert_into(ingredients::table)
ingredients.filter(id.eq(ingdt_id))
.first::<Ingredient>(conn)
.map_err(|e| format!("Could not retrieve : {}", e))
}
fn find(conn: &SqliteConnection, name: &str) -> Option<Ingredient> {
use self::schema::ingredients::dsl::*;
match ingredients.filter(alias.like(name))
.first(conn)
{
Ok(ingdt) => Some(ingdt),
Err(_) => None,
}
}
fn create(conn: &SqliteConnection, name: &str) -> Result<i32,String> {
use self::schema::ingredients::dsl::*;
let _ = diesel::insert_into(ingredients)
.values(&NewIngredient { alias: name })
.execute(conn)
.expect("Error inserting ingredient")
.map_err(|e| format!("Error inserting ingredient ! {:?}", e))?;
let inserted = ingredients
.order(id.desc())
.first::<Ingredient>(conn)
.map_err(|e| format!("No ingredient at all ! {:?}", e))?;
Ok(inserted.id)
}
}

View File

@@ -57,8 +57,7 @@ pub mod fields {
}
impl<DB: Backend> FromSql<SmallInt, DB> for RecipeCategory
where
i16: FromSql<SmallInt, DB>
where i16: FromSql<SmallInt, DB>
{
fn from_sql(bytes: Option<&DB::RawValue>) -> deserialize::Result<Self> {
let v = i16::from_sql(bytes)?;
@@ -68,13 +67,70 @@ pub mod fields {
}
impl<DB: Backend> ToSql<SmallInt, DB> for RecipeCategory
where
i16: ToSql<SmallInt, DB>{
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)
}
}
}
@@ -84,7 +140,7 @@ pub struct Recipe {
pub id: i32,
pub title: String,
pub category: fields::RecipeCategory,
pub ingredients: String,
pub ingredients: fields::IngredientList,
pub preparation: String,
}
@@ -93,20 +149,11 @@ pub struct Recipe {
pub struct NewRecipe<'a> {
pub title: &'a str,
pub category: fields::RecipeCategory,
pub ingredients: &'a str,
pub ingredients: fields::IngredientList,
pub preparation: &'a str,
}
impl<'a> NewRecipe<'a> {
pub fn new(title: &'a str, category: fields::RecipeCategory, ingredients: &'a str, preparation: &'a str) -> Self {
NewRecipe{
title,
category,
ingredients,
preparation,
}
}
pub fn insert(self, conn: &SqliteConnection) -> Result<Self, String> {
diesel::insert_into(recipes::table)
.values(&self)