use std::fmt::{self, Debug, Formatter};
use std::sync::Arc;
use crate::http::{Request, Response};
use crate::{ConnCtrl, Depot, Handler};
#[derive(Default)]
pub struct FlowCtrl {
catching: Option<bool>,
is_ceased: bool,
pub(crate) cursor: usize,
pub(crate) handlers: Vec<Option<Arc<dyn Handler>>>,
conn: ConnCtrl,
}
impl Debug for FlowCtrl {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("FlowCtrl")
.field("catching", &self.catching)
.field("is_ceased", &self.is_ceased)
.field("cursor", &self.cursor)
.finish()
}
}
impl FlowCtrl {
#[inline]
#[must_use]
pub fn new(handlers: Vec<Arc<dyn Handler>>) -> Self {
Self {
catching: None,
is_ceased: false,
cursor: 0,
handlers: handlers.into_iter().map(Some).collect(),
conn: ConnCtrl::new(),
}
}
#[inline]
#[must_use]
pub(crate) fn with_conn(handlers: Vec<Arc<dyn Handler>>, conn: ConnCtrl) -> Self {
Self {
catching: None,
is_ceased: false,
cursor: 0,
handlers: handlers.into_iter().map(Some).collect(),
conn,
}
}
#[inline]
#[must_use]
pub fn conn(&self) -> &ConnCtrl {
&self.conn
}
#[inline]
#[must_use]
pub fn has_next(&self) -> bool {
self.cursor < self.handlers.len()
}
#[inline]
pub async fn call_next(
&mut self,
req: &mut Request,
depot: &mut Depot,
res: &mut Response,
) -> bool {
if self.catching.is_none() {
self.catching = Some(res.is_stamped());
}
if !self.catching.unwrap_or_default() && res.is_stamped() {
self.skip_rest();
return false;
}
let start = self.cursor;
while self.cursor < self.handlers.len() {
let handler = self.handlers[self.cursor].take();
self.cursor += 1;
if let Some(handler) = handler {
handler.handle(req, depot, res, self).await;
if !self.catching.unwrap_or_default() && res.is_stamped() {
self.skip_rest();
return true;
}
}
}
self.cursor > start
}
#[inline]
pub fn skip_rest(&mut self) {
self.cursor = self.handlers.len()
}
#[inline]
#[must_use]
pub fn is_ceased(&self) -> bool {
self.is_ceased
}
#[inline]
pub fn cease(&mut self) {
self.skip_rest();
self.is_ceased = true;
}
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use crate::prelude::*;
use crate::routing::FlowCtrl;
use crate::{Depot, Handler, Request, Response};
#[tokio::test]
async fn test_reentrant_call_next() {
#[handler]
async fn around_a(
req: &mut Request,
depot: &mut Depot,
res: &mut Response,
ctrl: &mut FlowCtrl,
) {
depot.get_typed_mut::<Vec<&str>>().unwrap().push("enter_a");
assert!(ctrl.call_next(req, depot, res).await);
depot.get_typed_mut::<Vec<&str>>().unwrap().push("exit_a");
}
#[handler]
async fn around_b(
req: &mut Request,
depot: &mut Depot,
res: &mut Response,
ctrl: &mut FlowCtrl,
) {
depot.get_typed_mut::<Vec<&str>>().unwrap().push("enter_b");
assert!(ctrl.call_next(req, depot, res).await);
depot.get_typed_mut::<Vec<&str>>().unwrap().push("exit_b");
}
#[handler]
async fn goal(res: &mut Response, depot: &mut Depot) {
depot.get_typed_mut::<Vec<&str>>().unwrap().push("goal");
res.status_code(StatusCode::OK);
}
let order: Vec<&str> = Vec::new();
let handlers: Vec<Arc<dyn Handler>> =
vec![Arc::new(around_a), Arc::new(around_b), Arc::new(goal)];
let mut req = Request::new();
let mut depot = Depot::new();
depot.insert_typed(order);
let mut res = Response::new();
let mut ctrl = FlowCtrl::new(handlers);
let ran = ctrl.call_next(&mut req, &mut depot, &mut res).await;
assert!(ran);
assert_eq!(res.status_code, Some(StatusCode::OK));
assert!(!ctrl.has_next());
assert_eq!(
depot.get_typed::<Vec<&str>>().unwrap(),
&["enter_a", "enter_b", "goal", "exit_b", "exit_a"]
);
}
#[tokio::test]
async fn test_empty_handler_chain() {
let handlers: Vec<Arc<dyn Handler>> = Vec::new();
let mut req = Request::new();
let mut depot = Depot::new();
let mut res = Response::new();
let mut ctrl = FlowCtrl::new(handlers);
assert!(!ctrl.call_next(&mut req, &mut depot, &mut res).await);
assert!(!ctrl.has_next());
}
#[tokio::test]
async fn macro_handler_can_abort_connection() {
#[handler]
async fn abort(conn: &mut ConnCtrl) {
conn.abort();
}
let conn = ConnCtrl::new();
let mut ctrl = FlowCtrl::with_conn(vec![Arc::new(abort)], conn.clone());
let mut req = Request::new();
let mut depot = Depot::new();
let mut res = Response::new();
assert!(ctrl.call_next(&mut req, &mut depot, &mut res).await);
assert!(conn.is_aborted());
}
}