Adds basic vuejs functionnality, implements RecipeCategory custom field
This commit is contained in:
@@ -15,7 +15,7 @@ fn main() {
|
||||
|
||||
println!("Here are {} recipes :", result.len());
|
||||
for rec in result {
|
||||
println!("*************\n{}", rec.title);
|
||||
println!("*************\n{}\n({:?})", rec.title, rec.category);
|
||||
println!("-------------\n");
|
||||
println!("{}", rec.ingredients);
|
||||
}
|
||||
|
||||
@@ -4,11 +4,12 @@ extern crate diesel;
|
||||
use std::io::{Read, stdin};
|
||||
use diesel::SqliteConnection;
|
||||
use self::cookbook::*;
|
||||
use self::models::NewRecipe;
|
||||
use self::models::{NewRecipe, fields::RecipeCategory};
|
||||
|
||||
struct CreateRecipe<'a> {
|
||||
connection: &'a SqliteConnection,
|
||||
title: &'a str,
|
||||
category: Option<RecipeCategory>,
|
||||
ingredients: String,
|
||||
}
|
||||
|
||||
@@ -17,6 +18,7 @@ impl<'a> CreateRecipe<'a> {
|
||||
CreateRecipe{
|
||||
connection: conn,
|
||||
title: "New recipe",
|
||||
category: None,
|
||||
ingredients: String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,66 @@ use super::schema::recipes;
|
||||
use super::schema::ingredients;
|
||||
use super::diesel::prelude::*;
|
||||
|
||||
pub mod fields {
|
||||
use diesel::{
|
||||
backend::Backend,
|
||||
sqlite::Sqlite,
|
||||
sql_types::SmallInt,
|
||||
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, Clone, FromSqlRow, AsExpression)]
|
||||
#[repr(i16)]
|
||||
pub enum RecipeCategory {
|
||||
Breakfast = 0,
|
||||
Starter = 1,
|
||||
MainCourse = 2,
|
||||
Dessert = 3
|
||||
}
|
||||
|
||||
impl RecipeCategory {
|
||||
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 FromSql<SmallInt, Sqlite> for RecipeCategory {
|
||||
fn from_sql(bytes: Option<&<Sqlite as Backend>::RawValue>) -> deserialize::Result<Self> {
|
||||
if let Some(result) = RecipeCategory::from_id(
|
||||
<i16 as FromSql<SmallInt,Sqlite>>::from_sql(bytes)?)
|
||||
{ Ok(result) }
|
||||
else { Err("Invalid RecipeCategory id".into()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl ToSql<SmallInt, Sqlite> for RecipeCategory {
|
||||
fn to_sql<W: Write>(&self, out: &mut Output<W, Sqlite>) -> serialize::Result {
|
||||
let as_int = self.clone() as i16;
|
||||
<i16 as ToSql<SmallInt, Sqlite>>::to_sql(&as_int, out)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// Data for a recipe stored in DB
|
||||
#[derive(Debug, Clone, Queryable)]
|
||||
pub struct Recipe {
|
||||
pub id: i32,
|
||||
pub title: String,
|
||||
pub category: i32,
|
||||
pub category: fields::RecipeCategory,
|
||||
pub ingredients: String,
|
||||
pub preparation: String,
|
||||
}
|
||||
@@ -15,13 +70,13 @@ pub struct Recipe {
|
||||
#[table_name="recipes"]
|
||||
pub struct NewRecipe<'a> {
|
||||
pub title: &'a str,
|
||||
pub category: i32,
|
||||
pub category: i16,
|
||||
pub ingredients: &'a str,
|
||||
pub preparation: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> NewRecipe<'a> {
|
||||
pub fn new(title: &'a str, category: i32, ingredients: &'a str, preparation: &'a str) -> Self {
|
||||
pub fn new(title: &'a str, category: i16, ingredients: &'a str, preparation: &'a str) -> Self {
|
||||
NewRecipe{
|
||||
title,
|
||||
category,
|
||||
|
||||
@@ -9,7 +9,7 @@ table! {
|
||||
recipes (id) {
|
||||
id -> Integer,
|
||||
title -> Text,
|
||||
category -> Integer,
|
||||
category -> SmallInt,
|
||||
ingredients -> Text,
|
||||
preparation -> Text,
|
||||
}
|
||||
|
||||
@@ -7,23 +7,64 @@
|
||||
<body>
|
||||
<div id="app">
|
||||
<h1>Cook Assistant</h1>
|
||||
<p>Fetch value from wasm : {{ value }}</p>
|
||||
<strong>{{ message }}</strong>
|
||||
<!-- Details View -->
|
||||
<section v-if="active_view > -1">
|
||||
<button @click="setActiveView(-1)">X close</button>
|
||||
<h4>{{ items[active_view].title }}</h4>
|
||||
<p>{{ categories[items[active_view].category].name }}</p>
|
||||
</section>
|
||||
<!-- Category List View -->
|
||||
<section v-else>
|
||||
<div v-if="active_category == -1">
|
||||
<div v-for="c in categories" :key="c.id">
|
||||
<button @click="setActiveCategory(c.id)">{{ c.name }}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<button @click="setActiveCategory(-1)"><< back</button>
|
||||
<p>{{ categories[active_category].name }}</p>
|
||||
<ul>
|
||||
<li v-for="item in displayed" :key="item.id">
|
||||
<a href="" @click.prevent="setActiveView(item.id)">{{ item.title }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</body>
|
||||
<script type="text/javascript">
|
||||
|
||||
var getWasmValue = function() {
|
||||
return 42
|
||||
};
|
||||
|
||||
var app = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
message: 'Hello ! We are trying out here...',
|
||||
categories: [
|
||||
{id: 0, name: "Petit-déjeuner"},
|
||||
{id: 1, name: "Plat principal"},
|
||||
{id: 2, name: "Dessert"}
|
||||
],
|
||||
active_category: -1,
|
||||
active_view: -1,
|
||||
items: [
|
||||
{id: 0, title: "Raclette", category: 1},
|
||||
{id: 1, title: "Tartiflette", category: 1},
|
||||
{id: 2, title: "Pancakes", category: 0},
|
||||
{id: 3, title: "Crêpes", category: 2}
|
||||
],
|
||||
},
|
||||
methods: {
|
||||
setActiveCategory: function(id) {
|
||||
this.active_category = id;
|
||||
},
|
||||
setActiveView: function(id) {
|
||||
this.active_view = id;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
value: getWasmValue,
|
||||
displayed: function() {
|
||||
return this.items.filter(
|
||||
rec => rec.category == this.active_category
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user