1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
use crate::ResultFuture;

/// Type of the second parameter in a middleware.
///
/// `Next` is usually a closure capturing the next middleware, context and the next `Next`.
///
/// Developer of middleware can jump to next middleware by calling `next().await`.
///
/// ### Example
///
/// ```rust
/// use roa_core::App;
/// use async_std::task::spawn;
/// use http::StatusCode;
///
/// struct Symbol;
///
/// #[tokio::main]
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
///     let (addr, server) = App::new(())
///         .gate_fn(|ctx, next| async move {
///             ctx.store::<Symbol>("id", "1".to_string()).await;
///             next().await?;
///             assert_eq!("5", ctx.load::<Symbol>("id").await.unwrap().as_ref());
///             Ok(())
///         })
///         .gate_fn(|ctx, next| async move {
///             assert_eq!("1", ctx.load::<Symbol>("id").await.unwrap().as_ref());
///             ctx.store::<Symbol>("id", "2".to_string()).await;
///             next().await?;
///             assert_eq!("4", ctx.load::<Symbol>("id").await.unwrap().as_ref());
///             ctx.store::<Symbol>("id", "5".to_string()).await;
///             Ok(())
///         })
///         .gate_fn(|ctx, next| async move {
///             assert_eq!("2", ctx.load::<Symbol>("id").await.unwrap().as_ref());
///             ctx.store::<Symbol>("id", "3".to_string()).await;
///             next().await?; // next is none; do nothing
///             assert_eq!("3", ctx.load::<Symbol>("id").await.unwrap().as_ref());
///             ctx.store::<Symbol>("id", "4".to_string()).await;
///             Ok(())
///         })
///         .run_local()?;
///     spawn(server);
///     let resp = reqwest::get(&format!("http://{}", addr)).await?;
///     assert_eq!(StatusCode::OK, resp.status());
///     Ok(())
/// }
/// ```
///
/// ### Error Handling
///
/// You can catch or straightly throw a Error returned by next.
///
/// ```rust
/// use roa_core::{App, throw};
/// use async_std::task::spawn;
/// use http::StatusCode;
///
/// #[tokio::main]
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
///     let (addr, server) = App::new(())
///         .gate_fn(|ctx, next| async move {
///             // catch
///             if let Err(err) = next().await {
///                 // teapot is ok
///                 if err.status_code != StatusCode::IM_A_TEAPOT {
///                     return Err(err);
///                 }
///             }
///             Ok(())
///         })
///         .gate_fn(|ctx, next| async move {
///             next().await?; // just throw
///             unreachable!()
///         })
///         .gate_fn(|_ctx, _next| async move {
///             throw!(StatusCode::IM_A_TEAPOT, "I'm a teapot!")
///         })
///         .run_local()?;
///     spawn(server);
///     let resp = reqwest::get(&format!("http://{}", addr)).await?;
///     assert_eq!(StatusCode::OK, resp.status());
///     Ok(())
/// }
/// ```
pub type Next = Box<dyn FnOnce() -> ResultFuture + Sync + Send>;

pub fn last() -> ResultFuture {
    Box::pin(async move { Ok(()) })
}