Clean up todo example.

This commit is contained in:
Sergio Benitez 2020-06-08 12:11:34 -07:00
parent da34b66eb4
commit a4f5be93ef
2 changed files with 42 additions and 38 deletions

View File

@ -28,7 +28,10 @@ embed_migrations!();
pub struct DbConn(SqliteConnection); pub struct DbConn(SqliteConnection);
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
struct Context<'a>{ msg: Option<(&'a str, &'a str)>, tasks: Vec<Task> } struct Context<'a> {
msg: Option<(&'a str, &'a str)>,
tasks: Vec<Task>
}
impl<'a> Context<'a> { impl<'a> Context<'a> {
pub fn err(conn: &DbConn, msg: &'a str) -> Context<'a> { pub fn err(conn: &DbConn, msg: &'a str) -> Context<'a> {
@ -37,13 +40,16 @@ impl<'a> Context<'a> {
pub fn raw(conn: &DbConn, msg: Option<(&'a str, &'a str)>) -> Context<'a> { pub fn raw(conn: &DbConn, msg: Option<(&'a str, &'a str)>) -> Context<'a> {
match Task::all(conn) { match Task::all(conn) {
Ok(tasks) => Context{msg: msg, tasks}, Ok(tasks) => Context { msg, tasks},
Err(_) => Context{ Err(e) => {
error_!("DB Task::all() error: {}", e);
Context {
msg: Some(("error", "Couldn't access the task database.")), msg: Some(("error", "Couldn't access the task database.")),
tasks: vec![] tasks: vec![]
} }
} }
} }
}
} }
#[post("/", data = "<todo_form>")] #[post("/", data = "<todo_form>")]
@ -52,7 +58,8 @@ fn new(todo_form: Form<Todo>, conn: DbConn) -> Flash<Redirect> {
if todo.description.is_empty() { if todo.description.is_empty() {
Flash::error(Redirect::to("/"), "Description cannot be empty.") Flash::error(Redirect::to("/"), "Description cannot be empty.")
} else if let Err(e) = Task::insert(todo, &conn) { } else if let Err(e) = Task::insert(todo, &conn) {
Flash::error(Redirect::to("/"), &format!("Database error: {}", e)) error_!("DB insertion error: {}", e);
Flash::error(Redirect::to("/"), "Todo could not be inserted due an internal error.")
} else { } else {
Flash::success(Redirect::to("/"), "Todo successfully added.") Flash::success(Redirect::to("/"), "Todo successfully added.")
} }
@ -60,23 +67,27 @@ fn new(todo_form: Form<Todo>, conn: DbConn) -> Flash<Redirect> {
#[put("/<id>")] #[put("/<id>")]
fn toggle(id: i32, conn: DbConn) -> Result<Redirect, Template> { fn toggle(id: i32, conn: DbConn) -> Result<Redirect, Template> {
match Task::toggle_with_id(id, &conn) { Task::toggle_with_id(id, &conn)
Ok(()) => Ok(Redirect::to("/")), .map(|_| Redirect::to("/"))
Err(e) => Err(Template::render("index", &Context::err(&conn, &format!("Couldn't toggle task: {}", e)))), .map_err(|e| {
} error_!("DB toggle({}) error: {}", id, e);
Template::render("index", Context::err(&conn, "Failed to toggle task."))
})
} }
#[delete("/<id>")] #[delete("/<id>")]
fn delete(id: i32, conn: DbConn) -> Result<Flash<Redirect>, Template> { fn delete(id: i32, conn: DbConn) -> Result<Flash<Redirect>, Template> {
match Task::delete_with_id(id, &conn) { Task::delete_with_id(id, &conn)
Ok(()) => Ok(Flash::success(Redirect::to("/"), "Todo was deleted.")), .map(|_| Flash::success(Redirect::to("/"), "Todo was deleted."))
Err(e) => Err(Template::render("index", &Context::err(&conn, &format!("Couldn't delete task: {}", e)))), .map_err(|e| {
} error_!("DB deletion({}) error: {}", id, e);
Template::render("index", Context::err(&conn, "Failed to delete task."))
})
} }
#[get("/")] #[get("/")]
fn index(msg: Option<FlashMessage<'_, '_>>, conn: DbConn) -> Template { fn index(msg: Option<FlashMessage<'_, '_>>, conn: DbConn) -> Template {
Template::render("index", &match msg { Template::render("index", match msg {
Some(ref msg) => Context::raw(&conn, Some((msg.name(), msg.msg()))), Some(ref msg) => Context::raw(&conn, Some((msg.name(), msg.msg()))),
None => Context::raw(&conn, None), None => Context::raw(&conn, None),
}) })

View File

@ -1,5 +1,4 @@
use diesel::{self, result::Error as DieselError, prelude::*}; use diesel::{self, result::QueryResult, prelude::*};
type Result<T> = std::result::Result<T, DieselError>;
mod schema { mod schema {
table! { table! {
@ -22,44 +21,38 @@ pub struct Task {
pub completed: bool pub completed: bool
} }
#[derive(FromForm)] #[derive(Debug, FromForm)]
pub struct Todo { pub struct Todo {
pub description: String, pub description: String,
} }
impl Task { impl Task {
pub fn all(conn: &SqliteConnection) -> Result<Vec<Task>> { pub fn all(conn: &SqliteConnection) -> QueryResult<Vec<Task>> {
all_tasks.order(tasks::id.desc()).load::<Task>(conn) all_tasks.order(tasks::id.desc()).load::<Task>(conn)
} }
pub fn insert(todo: Todo, conn: &SqliteConnection) -> Result<()> { /// Returns the number of affected rows: 1.
pub fn insert(todo: Todo, conn: &SqliteConnection) -> QueryResult<usize> {
let t = Task { id: None, description: todo.description, completed: false }; let t = Task { id: None, description: todo.description, completed: false };
diesel::insert_into(tasks::table).values(&t).execute(conn)?; diesel::insert_into(tasks::table).values(&t).execute(conn)
Ok(())
} }
pub fn toggle_with_id(id: i32, conn: &SqliteConnection) -> Result<()> { /// Returns the number of affected rows: 1.
pub fn toggle_with_id(id: i32, conn: &SqliteConnection) -> QueryResult<usize> {
let task = all_tasks.find(id).get_result::<Task>(conn)?; let task = all_tasks.find(id).get_result::<Task>(conn)?;
let new_status = !task.completed; let new_status = !task.completed;
let updated_task = diesel::update(all_tasks.find(id)); let updated_task = diesel::update(all_tasks.find(id));
updated_task.set(task_completed.eq(new_status)).execute(conn)?; updated_task.set(task_completed.eq(new_status)).execute(conn)
Ok(())
} }
pub fn delete_with_id(id: i32, conn: &SqliteConnection) -> Result<()> { /// Returns the number of affected rows: 1.
// `.execute` returns a usize representing the number of pub fn delete_with_id(id: i32, conn: &SqliteConnection) -> QueryResult<usize> {
// database rows affected by this modification. diesel::delete(all_tasks.find(id)).execute(conn)
// In a larger app these metrics may be useful,
// but for a simple example we'll ignore them.
diesel::delete(all_tasks.find(id)).execute(conn).map(|_| ())
} }
/// Returns the number of affected rows.
#[cfg(test)] #[cfg(test)]
pub fn delete_all(conn: &SqliteConnection) -> Result<()> { pub fn delete_all(conn: &SqliteConnection) -> QueryResult<usize> {
// see comment in `.delete_with_id`. diesel::delete(all_tasks).execute(conn)
diesel::delete(all_tasks).execute(conn).map(|_| ())
} }
} }