about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVincent Ambo <tazjin@gmail.com>2018-04-11T22·04+0200
committerVincent Ambo <tazjin@gmail.com>2018-04-11T22·04+0200
commit0ec214423e0367ad3be21a7c9df5341da3c633c4 (patch)
treee2a5456d4a44b484429bde640d8e76ba6909e939
parenta9917d54a82c3ff0dc35b412a85093028d340902 (diff)
fix(handlers): Fix chained error handling in actors
This took me some time to figure out so it's useful to document in the
commit message.

When chaining messages from actors, the result type of a message (i.e.
the actual `<M as Message>::Result`) is sometimes itself a
`Result<T, E>`.

In many cases this leads to a situation where the return type of a
message sending process is something like (simplified):

    Future<Item=Result<Foo, ConverseError>, Error=actix::MailboxError>

Due to the implementation of
`From<actix::MailboxError> for ConverseError` it is possible to use
`.from_err()` on these futures to convert the future's `Error` to
`ConverseError`.

The type `Result` apparently implements `IntoFuture`, which means that
due to some trait magic that's been applied somewhere in the futures
API a call to `flatten()` can "lift" the contained error if the error
types match and give us a "simple"

    Future<Item=Foo, Error=ConverseError>

From that point on chaining becomes easy.
-rw-r--r--src/handlers.rs29
1 files changed, 15 insertions, 14 deletions
diff --git a/src/handlers.rs b/src/handlers.rs
index 71469deea7d5..43e45d925463 100644
--- a/src/handlers.rs
+++ b/src/handlers.rs
@@ -34,9 +34,12 @@ pub struct AppState {
 
 pub fn forum_index(state: State<AppState>) -> ConverseResponse {
     state.db.send(ListThreads)
-        .and_then(move |res| state.renderer.send(IndexPage { threads: res.unwrap() }))
-        .from_err()
-        .map(|res| HttpResponse::Ok().content_type(HTML).body(res.unwrap()))
+        .flatten()
+        .and_then(move |res| state.renderer.send(IndexPage {
+            threads: res
+        }).from_err())
+        .flatten()
+        .map(|res| HttpResponse::Ok().content_type(HTML).body(res))
         .responder()
 }
 
@@ -44,22 +47,20 @@ pub fn forum_index(state: State<AppState>) -> ConverseResponse {
 pub fn forum_thread(state: State<AppState>, thread_id: Path<i32>) -> ConverseResponse {
     let id = thread_id.into_inner();
     state.db.send(GetThread(id))
-        .and_then(move |res| {
-            let u = res.unwrap();
-            state.renderer.send(ThreadPage {
-                thread: u.0,
-                posts: u.1,
-            })
-        })
-        .from_err()
-        .map(|res| HttpResponse::Ok().content_type(HTML).body(res.unwrap()))
+        .flatten()
+        .and_then(move |res| state.renderer.send(ThreadPage {
+            thread: res.0,
+            posts: res.1,
+        }).from_err())
+        .flatten()
+        .map(|res| HttpResponse::Ok().content_type(HTML).body(res))
         .responder()
 }
 
 /// This handler presents the user with the "New Thread" form.
 pub fn new_thread(state: State<AppState>) -> ConverseResponse {
-    state.renderer.send(NewThreadPage).from_err()
-        .map(|res| HttpResponse::Ok().content_type(HTML).body(res.unwrap()))
+    state.renderer.send(NewThreadPage).flatten()
+        .map(|res| HttpResponse::Ok().content_type(HTML).body(res))
         .responder()
 }