Works on ingredients
This commit is contained in:
Binary file not shown.
@@ -2,7 +2,7 @@
|
||||
CREATE TABLE recipes (
|
||||
id INTEGER PRIMARY KEY NOT NULL,
|
||||
title VARCHAR NOT NULL,
|
||||
category INTEGER NOT NULL,
|
||||
category SMALLINT NOT NULL,
|
||||
ingredients TEXT NOT NULL,
|
||||
preparation TEXT NOT NULL
|
||||
)
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user