mirror of https://github.com/rwf2/Rocket.git
Use 'Result's instead of 'bool's in todo example.
This commit is contained in:
parent
bbad427079
commit
da34b66eb4
|
@ -28,15 +28,21 @@ embed_migrations!();
|
||||||
pub struct DbConn(SqliteConnection);
|
pub struct DbConn(SqliteConnection);
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
struct Context<'a, 'b>{ msg: Option<(&'a str, &'b str)>, tasks: Vec<Task> }
|
struct Context<'a>{ msg: Option<(&'a str, &'a str)>, tasks: Vec<Task> }
|
||||||
|
|
||||||
impl<'a, 'b> Context<'a, 'b> {
|
impl<'a> Context<'a> {
|
||||||
pub fn err(conn: &DbConn, msg: &'a str) -> Context<'static, 'a> {
|
pub fn err(conn: &DbConn, msg: &'a str) -> Context<'a> {
|
||||||
Context{msg: Some(("error", msg)), tasks: Task::all(conn)}
|
Context{msg: Some(("error", msg)), tasks: Task::all(conn).unwrap_or_default()}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn raw(conn: &DbConn, msg: Option<(&'a str, &'b str)>) -> Context<'a, 'b> {
|
pub fn raw(conn: &DbConn, msg: Option<(&'a str, &'a str)>) -> Context<'a> {
|
||||||
Context{msg: msg, tasks: Task::all(conn)}
|
match Task::all(conn) {
|
||||||
|
Ok(tasks) => Context{msg: msg, tasks},
|
||||||
|
Err(_) => Context{
|
||||||
|
msg: Some(("error", "Couldn't access the task database.")),
|
||||||
|
tasks: vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,28 +51,26 @@ fn new(todo_form: Form<Todo>, conn: DbConn) -> Flash<Redirect> {
|
||||||
let todo = todo_form.into_inner();
|
let todo = todo_form.into_inner();
|
||||||
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 Task::insert(todo, &conn) {
|
} else if let Err(e) = Task::insert(todo, &conn) {
|
||||||
Flash::success(Redirect::to("/"), "Todo successfully added.")
|
Flash::error(Redirect::to("/"), &format!("Database error: {}", e))
|
||||||
} else {
|
} else {
|
||||||
Flash::error(Redirect::to("/"), "Whoops! The server failed.")
|
Flash::success(Redirect::to("/"), "Todo successfully added.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[put("/<id>")]
|
#[put("/<id>")]
|
||||||
fn toggle(id: i32, conn: DbConn) -> Result<Redirect, Template> {
|
fn toggle(id: i32, conn: DbConn) -> Result<Redirect, Template> {
|
||||||
if Task::toggle_with_id(id, &conn) {
|
match Task::toggle_with_id(id, &conn) {
|
||||||
Ok(Redirect::to("/"))
|
Ok(()) => Ok(Redirect::to("/")),
|
||||||
} else {
|
Err(e) => Err(Template::render("index", &Context::err(&conn, &format!("Couldn't toggle task: {}", e)))),
|
||||||
Err(Template::render("index", &Context::err(&conn, "Couldn't 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> {
|
||||||
if Task::delete_with_id(id, &conn) {
|
match Task::delete_with_id(id, &conn) {
|
||||||
Ok(Flash::success(Redirect::to("/"), "Todo was deleted."))
|
Ok(()) => Ok(Flash::success(Redirect::to("/"), "Todo was deleted.")),
|
||||||
} else {
|
Err(e) => Err(Template::render("index", &Context::err(&conn, &format!("Couldn't delete task: {}", e)))),
|
||||||
Err(Template::render("index", &Context::err(&conn, "Couldn't delete task.")))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use diesel::{self, prelude::*};
|
use diesel::{self, result::Error as DieselError, prelude::*};
|
||||||
|
type Result<T> = std::result::Result<T, DieselError>;
|
||||||
|
|
||||||
mod schema {
|
mod schema {
|
||||||
table! {
|
table! {
|
||||||
|
@ -27,32 +28,38 @@ pub struct Todo {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Task {
|
impl Task {
|
||||||
pub fn all(conn: &SqliteConnection) -> Vec<Task> {
|
pub fn all(conn: &SqliteConnection) -> Result<Vec<Task>> {
|
||||||
all_tasks.order(tasks::id.desc()).load::<Task>(conn).unwrap()
|
all_tasks.order(tasks::id.desc()).load::<Task>(conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert(todo: Todo, conn: &SqliteConnection) -> bool {
|
pub fn insert(todo: Todo, conn: &SqliteConnection) -> Result<()> {
|
||||||
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).is_ok()
|
diesel::insert_into(tasks::table).values(&t).execute(conn)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn toggle_with_id(id: i32, conn: &SqliteConnection) -> bool {
|
pub fn toggle_with_id(id: i32, conn: &SqliteConnection) -> Result<()> {
|
||||||
let task = all_tasks.find(id).get_result::<Task>(conn);
|
let task = all_tasks.find(id).get_result::<Task>(conn)?;
|
||||||
if task.is_err() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let new_status = !task.unwrap().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).is_ok()
|
updated_task.set(task_completed.eq(new_status)).execute(conn)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_with_id(id: i32, conn: &SqliteConnection) -> bool {
|
pub fn delete_with_id(id: i32, conn: &SqliteConnection) -> Result<()> {
|
||||||
diesel::delete(all_tasks.find(id)).execute(conn).is_ok()
|
// `.execute` returns a usize representing the number of
|
||||||
|
// database rows affected by this modification.
|
||||||
|
// 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(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn delete_all(conn: &SqliteConnection) -> bool {
|
pub fn delete_all(conn: &SqliteConnection) -> Result<()> {
|
||||||
diesel::delete(all_tasks).execute(conn).is_ok()
|
// see comment in `.delete_with_id`.
|
||||||
|
diesel::delete(all_tasks).execute(conn).map(|_| ())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ macro_rules! run_test {
|
||||||
let db = super::DbConn::get_one(&rocket);
|
let db = super::DbConn::get_one(&rocket);
|
||||||
let $client = Client::new(rocket).expect("Rocket client");
|
let $client = Client::new(rocket).expect("Rocket client");
|
||||||
let $conn = db.expect("failed to get database connection for testing");
|
let $conn = db.expect("failed to get database connection for testing");
|
||||||
assert!(Task::delete_all(&$conn), "failed to delete all tasks for testing");
|
Task::delete_all(&$conn).expect("failed to delete all tasks for testing");
|
||||||
|
|
||||||
$block
|
$block
|
||||||
})
|
})
|
||||||
|
@ -28,7 +28,7 @@ macro_rules! run_test {
|
||||||
fn test_insertion_deletion() {
|
fn test_insertion_deletion() {
|
||||||
run_test!(|client, conn| {
|
run_test!(|client, conn| {
|
||||||
// Get the tasks before making changes.
|
// Get the tasks before making changes.
|
||||||
let init_tasks = Task::all(&conn);
|
let init_tasks = Task::all(&conn).unwrap();
|
||||||
|
|
||||||
// Issue a request to insert a new task.
|
// Issue a request to insert a new task.
|
||||||
client.post("/todo")
|
client.post("/todo")
|
||||||
|
@ -37,7 +37,7 @@ fn test_insertion_deletion() {
|
||||||
.dispatch();
|
.dispatch();
|
||||||
|
|
||||||
// Ensure we have one more task in the database.
|
// Ensure we have one more task in the database.
|
||||||
let new_tasks = Task::all(&conn);
|
let new_tasks = Task::all(&conn).unwrap();
|
||||||
assert_eq!(new_tasks.len(), init_tasks.len() + 1);
|
assert_eq!(new_tasks.len(), init_tasks.len() + 1);
|
||||||
|
|
||||||
// Ensure the task is what we expect.
|
// Ensure the task is what we expect.
|
||||||
|
@ -49,7 +49,7 @@ fn test_insertion_deletion() {
|
||||||
client.delete(format!("/todo/{}", id)).dispatch();
|
client.delete(format!("/todo/{}", id)).dispatch();
|
||||||
|
|
||||||
// Ensure it's gone.
|
// Ensure it's gone.
|
||||||
let final_tasks = Task::all(&conn);
|
let final_tasks = Task::all(&conn).unwrap();
|
||||||
assert_eq!(final_tasks.len(), init_tasks.len());
|
assert_eq!(final_tasks.len(), init_tasks.len());
|
||||||
if final_tasks.len() > 0 {
|
if final_tasks.len() > 0 {
|
||||||
assert_ne!(final_tasks[0].description, "My first task");
|
assert_ne!(final_tasks[0].description, "My first task");
|
||||||
|
@ -66,16 +66,16 @@ fn test_toggle() {
|
||||||
.body("description=test_for_completion")
|
.body("description=test_for_completion")
|
||||||
.dispatch();
|
.dispatch();
|
||||||
|
|
||||||
let task = Task::all(&conn)[0].clone();
|
let task = Task::all(&conn).unwrap()[0].clone();
|
||||||
assert_eq!(task.completed, false);
|
assert_eq!(task.completed, false);
|
||||||
|
|
||||||
// Issue a request to toggle the task; ensure it is completed.
|
// Issue a request to toggle the task; ensure it is completed.
|
||||||
client.put(format!("/todo/{}", task.id.unwrap())).dispatch();
|
client.put(format!("/todo/{}", task.id.unwrap())).dispatch();
|
||||||
assert_eq!(Task::all(&conn)[0].completed, true);
|
assert_eq!(Task::all(&conn).unwrap()[0].completed, true);
|
||||||
|
|
||||||
// Issue a request to toggle the task; ensure it's not completed again.
|
// Issue a request to toggle the task; ensure it's not completed again.
|
||||||
client.put(format!("/todo/{}", task.id.unwrap())).dispatch();
|
client.put(format!("/todo/{}", task.id.unwrap())).dispatch();
|
||||||
assert_eq!(Task::all(&conn)[0].completed, false);
|
assert_eq!(Task::all(&conn).unwrap()[0].completed, false);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ fn test_many_insertions() {
|
||||||
let rng = thread_rng();
|
let rng = thread_rng();
|
||||||
run_test!(|client, conn| {
|
run_test!(|client, conn| {
|
||||||
// Get the number of tasks initially.
|
// Get the number of tasks initially.
|
||||||
let init_num = Task::all(&conn).len();
|
let init_num = Task::all(&conn).unwrap().len();
|
||||||
let mut descs = Vec::new();
|
let mut descs = Vec::new();
|
||||||
|
|
||||||
for i in 0..ITER {
|
for i in 0..ITER {
|
||||||
|
@ -101,7 +101,7 @@ fn test_many_insertions() {
|
||||||
descs.insert(0, desc);
|
descs.insert(0, desc);
|
||||||
|
|
||||||
// Ensure the task was inserted properly and all other tasks remain.
|
// Ensure the task was inserted properly and all other tasks remain.
|
||||||
let tasks = Task::all(&conn);
|
let tasks = Task::all(&conn).unwrap();
|
||||||
assert_eq!(tasks.len(), init_num + i + 1);
|
assert_eq!(tasks.len(), init_num + i + 1);
|
||||||
|
|
||||||
for j in 0..i {
|
for j in 0..i {
|
||||||
|
|
Loading…
Reference in New Issue