use crate::{
handler::{Handler, IntoHandler},
hl::{
bootstrap::{DualFilter, RequestFilter},
context::{RequestContext, ResponseContext},
state::{CreateHandler, DoneHandler, EmptyCreateHandler},
IntoFlow,
},
};
use crate::hl::bootstrap::ResponseFilter;
#[cfg(feature = "experimental_websocket")]
use crate::hl::bootstrap::WebSocketFilter;
#[cfg(feature = "experimental_websocket")]
pub struct FilterBuilder<
Sf = EmptyCreateHandler,
Req = (),
Res = (),
Done = (),
WsUp = (),
WsDown = (),
> {
state_factory: Sf,
request_filter: Req,
response_filter: Res,
done_handler: Done,
websocket_upstream: WsUp,
websocket_downstream: WsDown,
}
#[cfg(not(feature = "experimental_websocket"))]
pub struct FilterBuilder<Sf = EmptyCreateHandler, Req = (), Res = (), Done = ()> {
state_factory: Sf,
request_filter: Req,
response_filter: Res,
done_handler: Done,
}
impl FilterBuilder {
pub fn new() -> Self {
Self {
state_factory: EmptyCreateHandler,
request_filter: (),
response_filter: (),
done_handler: (),
#[cfg(feature = "experimental_websocket")]
websocket_upstream: (),
#[cfg(feature = "experimental_websocket")]
websocket_downstream: (),
}
}
#[cfg(not(feature = "experimental_websocket"))]
pub fn on_create<Sf: CreateHandler>(self, state_factory: Sf) -> FilterBuilder<Sf, (), (), ()> {
FilterBuilder {
state_factory,
request_filter: self.request_filter,
response_filter: self.response_filter,
done_handler: self.done_handler,
}
}
#[cfg(feature = "experimental_websocket")]
pub fn on_create<Sf: CreateHandler>(
self,
state_factory: Sf,
) -> FilterBuilder<Sf, (), (), (), (), ()> {
FilterBuilder {
state_factory,
request_filter: self.request_filter,
response_filter: self.response_filter,
done_handler: self.done_handler,
websocket_upstream: self.websocket_upstream,
websocket_downstream: self.websocket_downstream,
}
}
}
impl Default for FilterBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(not(feature = "experimental_websocket"))]
impl<Sf, Res, Done> FilterBuilder<Sf, (), Res, Done>
where
Sf: CreateHandler,
{
pub fn on_request<I, Req>(
self,
request_filter: Req,
) -> FilterBuilder<Sf, Req::Handler, Res, Done>
where
Req: IntoHandler<RequestContext<Sf::State>, I>,
{
FilterBuilder {
state_factory: self.state_factory,
request_filter: request_filter.into_handler(),
response_filter: self.response_filter,
done_handler: self.done_handler,
}
}
}
#[cfg(not(feature = "experimental_websocket"))]
impl<Sf, Done> FilterBuilder<Sf, (), (), Done>
where
Sf: CreateHandler,
{
pub fn on_response<I, Res>(
self,
response_filter: Res,
) -> FilterBuilder<Sf, (), Res::Handler, Done>
where
Res: IntoHandler<ResponseContext<(), Sf::State>, I, Output = ()>,
{
FilterBuilder {
state_factory: self.state_factory,
request_filter: self.request_filter,
response_filter: response_filter.into_handler(),
done_handler: self.done_handler,
}
}
}
#[cfg(feature = "experimental_websocket")]
impl<Sf, Res, Done, WsUp, WsDown> FilterBuilder<Sf, (), Res, Done, WsUp, WsDown>
where
Sf: CreateHandler,
{
pub fn on_done<D>(self, done_handler: D) -> FilterBuilder<Sf, (), Res, D, WsUp, WsDown>
where
D: DoneHandler<Sf::State>,
{
FilterBuilder {
state_factory: self.state_factory,
request_filter: self.request_filter,
response_filter: self.response_filter,
done_handler,
websocket_upstream: self.websocket_upstream,
websocket_downstream: self.websocket_downstream,
}
}
}
#[cfg(feature = "experimental_websocket")]
impl<Sf, Res, Done, WsUp, WsDown> FilterBuilder<Sf, (), Res, Done, WsUp, WsDown>
where
Sf: CreateHandler,
{
pub fn on_request<I, Req>(
self,
request_filter: Req,
) -> FilterBuilder<Sf, Req::Handler, Res, Done, WsUp, WsDown>
where
Req: IntoHandler<RequestContext<Sf::State>, I>,
{
FilterBuilder {
state_factory: self.state_factory,
request_filter: request_filter.into_handler(),
response_filter: self.response_filter,
done_handler: self.done_handler,
websocket_upstream: self.websocket_upstream,
websocket_downstream: self.websocket_downstream,
}
}
}
#[cfg(feature = "experimental_websocket")]
impl<Sf, Done, WsUp, WsDown> FilterBuilder<Sf, (), (), Done, WsUp, WsDown>
where
Sf: CreateHandler,
{
pub fn on_response<I, Res>(
self,
response_filter: Res,
) -> FilterBuilder<Sf, (), Res::Handler, Done, WsUp, WsDown>
where
Res: IntoHandler<ResponseContext<(), Sf::State>, I, Output = ()>,
{
FilterBuilder {
state_factory: self.state_factory,
request_filter: self.request_filter,
response_filter: response_filter.into_handler(),
done_handler: self.done_handler,
websocket_upstream: self.websocket_upstream,
websocket_downstream: self.websocket_downstream,
}
}
}
#[cfg(not(feature = "experimental_websocket"))]
impl<Sf, Req, Done> FilterBuilder<Sf, Req, (), Done>
where
Sf: CreateHandler,
Req: Handler<RequestContext<Sf::State>, Output: IntoFlow>,
{
pub fn on_response<I, Res>(
self,
response_filter: Res,
) -> FilterBuilder<Sf, Req, Res::Handler, Done>
where
Res: IntoHandler<
ResponseContext<<Req::Output as IntoFlow>::RequestData, Sf::State>,
I,
Output: IntoFlow,
>,
{
FilterBuilder {
state_factory: self.state_factory,
request_filter: self.request_filter,
response_filter: response_filter.into_handler(),
done_handler: self.done_handler,
}
}
}
#[cfg(feature = "experimental_websocket")]
impl<Sf, Req, Done, WsUp, WsDown> FilterBuilder<Sf, Req, (), Done, WsUp, WsDown>
where
Sf: CreateHandler,
Req: Handler<RequestContext<Sf::State>, Output: IntoFlow>,
{
pub fn on_response<I, Res>(
self,
response_filter: Res,
) -> FilterBuilder<Sf, Req, Res::Handler, Done, WsUp, WsDown>
where
Res: IntoHandler<
ResponseContext<<Req::Output as IntoFlow>::RequestData, Sf::State>,
I,
Output: IntoFlow,
>,
{
FilterBuilder {
state_factory: self.state_factory,
request_filter: self.request_filter,
response_filter: response_filter.into_handler(),
done_handler: self.done_handler,
websocket_upstream: self.websocket_upstream,
websocket_downstream: self.websocket_downstream,
}
}
pub fn on_done<D>(self, done_handler: D) -> FilterBuilder<Sf, Req, (), D, WsUp, WsDown>
where
D: DoneHandler<Sf::State>,
{
FilterBuilder {
state_factory: self.state_factory,
request_filter: self.request_filter,
response_filter: self.response_filter,
done_handler,
websocket_upstream: self.websocket_upstream,
websocket_downstream: self.websocket_downstream,
}
}
}
#[cfg(not(feature = "experimental_websocket"))]
impl<Sf, Req, Done> FilterBuilder<Sf, Req, (), Done>
where
Req: Handler<RequestContext<Sf::State>>,
Sf: CreateHandler,
{
pub fn build(self) -> RequestFilter<Req, Sf, Done> {
RequestFilter {
request_handler: self.request_filter,
state_factory: self.state_factory,
done_handler: self.done_handler,
}
}
pub fn on_done<D>(self, done_handler: D) -> FilterBuilder<Sf, Req, (), D>
where
D: DoneHandler<Sf::State>,
{
FilterBuilder {
state_factory: self.state_factory,
request_filter: self.request_filter,
response_filter: self.response_filter,
done_handler,
}
}
}
#[cfg(not(feature = "experimental_websocket"))]
impl<Sf, Res, Done> FilterBuilder<Sf, (), Res, Done>
where
Sf: CreateHandler,
{
pub fn on_done<D>(self, done_handler: D) -> FilterBuilder<Sf, (), Res, D>
where
D: DoneHandler<Sf::State>,
{
FilterBuilder {
state_factory: self.state_factory,
request_filter: self.request_filter,
response_filter: self.response_filter,
done_handler,
}
}
}
#[cfg(not(feature = "experimental_websocket"))]
impl<Sf, Res, Done> FilterBuilder<Sf, (), Res, Done>
where
Res: Handler<ResponseContext<(), Sf::State>>,
Sf: CreateHandler,
{
pub fn build(self) -> ResponseFilter<Res, Sf, Done> {
ResponseFilter {
response_handler: self.response_filter,
state_factory: self.state_factory,
done_handler: self.done_handler,
}
}
}
#[cfg(not(feature = "experimental_websocket"))]
impl<Sf, Req, Res, Done> FilterBuilder<Sf, Req, Res, Done>
where
Req: Handler<RequestContext<Sf::State>, Output: IntoFlow>,
Res: Handler<ResponseContext<<Req::Output as IntoFlow>::RequestData, Sf::State>>,
Sf: CreateHandler,
{
pub fn build(self) -> DualFilter<Req, Res, Sf, Done> {
DualFilter {
request_handler: self.request_filter,
response_handler: self.response_filter,
state_factory: self.state_factory,
done_handler: self.done_handler,
}
}
}
#[cfg(feature = "experimental_websocket")]
impl<Sf, Req, Done, WsUp, WsDown> FilterBuilder<Sf, Req, (), Done, WsUp, WsDown>
where
Req: Handler<RequestContext<Sf::State>, Output: IntoFlow>,
Sf: CreateHandler,
{
pub fn build(self) -> WebSocketFilter<RequestFilter<Req, Sf, Done>, Sf, WsUp, WsDown> {
let base_filter = RequestFilter {
request_handler: self.request_filter,
state_factory: self.state_factory,
done_handler: self.done_handler,
};
WebSocketFilter {
base_filter,
websocket_upstream: self.websocket_upstream,
websocket_downstream: self.websocket_downstream,
_sf: std::marker::PhantomData,
}
}
}
#[cfg(feature = "experimental_websocket")]
impl<Sf, Req, Res, Done, WsUp, WsDown> FilterBuilder<Sf, Req, Res, Done, WsUp, WsDown>
where
Req: Handler<RequestContext<Sf::State>, Output: IntoFlow>,
Res: Handler<ResponseContext<<Req::Output as IntoFlow>::RequestData, Sf::State>>,
Sf: CreateHandler,
{
pub fn build(self) -> WebSocketFilter<DualFilter<Req, Res, Sf, Done>, Sf, WsUp, WsDown> {
let base_filter = DualFilter {
request_handler: self.request_filter,
response_handler: self.response_filter,
state_factory: self.state_factory,
done_handler: self.done_handler,
};
WebSocketFilter {
base_filter,
websocket_upstream: self.websocket_upstream,
websocket_downstream: self.websocket_downstream,
_sf: std::marker::PhantomData,
}
}
}
#[cfg(not(feature = "experimental_websocket"))]
impl<Sf, Req, Res, Done> FilterBuilder<Sf, Req, Res, Done>
where
Req: Handler<RequestContext<Sf::State>, Output: IntoFlow>,
Res: Handler<ResponseContext<<Req::Output as IntoFlow>::RequestData, Sf::State>>,
Sf: CreateHandler,
{
pub fn on_done<D>(self, done_handler: D) -> FilterBuilder<Sf, Req, Res, D>
where
D: DoneHandler<Sf::State>,
{
FilterBuilder {
state_factory: self.state_factory,
request_filter: self.request_filter,
response_filter: self.response_filter,
done_handler,
}
}
}
#[cfg(feature = "experimental_websocket")]
impl<Sf, Req, Res, Done, WsUp, WsDown> FilterBuilder<Sf, Req, Res, Done, WsUp, WsDown>
where
Req: Handler<RequestContext<Sf::State>, Output: IntoFlow>,
Res: Handler<ResponseContext<<Req::Output as IntoFlow>::RequestData, Sf::State>>,
Sf: CreateHandler,
{
pub fn on_done<D>(self, done_handler: D) -> FilterBuilder<Sf, Req, Res, D, WsUp, WsDown>
where
D: DoneHandler<Sf::State>,
{
FilterBuilder {
state_factory: self.state_factory,
request_filter: self.request_filter,
response_filter: self.response_filter,
done_handler,
websocket_upstream: self.websocket_upstream,
websocket_downstream: self.websocket_downstream,
}
}
}
#[cfg(feature = "experimental_websocket")]
impl<Sf, Res, Done, WsUp, WsDown> FilterBuilder<Sf, (), Res, Done, WsUp, WsDown>
where
Res: Handler<ResponseContext<(), Sf::State>>,
Sf: CreateHandler,
{
pub fn build(self) -> WebSocketFilter<ResponseFilter<Res, Sf, Done>, Sf, WsUp, WsDown> {
let base_filter = ResponseFilter {
response_handler: self.response_filter,
state_factory: self.state_factory,
done_handler: self.done_handler,
};
WebSocketFilter {
base_filter,
websocket_upstream: self.websocket_upstream,
websocket_downstream: self.websocket_downstream,
_sf: std::marker::PhantomData,
}
}
}
#[cfg(feature = "experimental_websocket")]
impl<Sf, Req, Res, Done, WsUp, WsDown> FilterBuilder<Sf, Req, Res, Done, WsUp, WsDown>
where
Sf: CreateHandler,
{
pub fn on_upgrade_upstream<WsUpHnd, I>(
self,
websocket_upstream: WsUpHnd,
) -> FilterBuilder<Sf, Req, Res, Done, WsUpHnd::Handler, WsDown>
where
WsUpHnd: IntoHandler<
crate::extract::context::UpgradeUpstreamContext<Sf::State>,
I,
Output: crate::handler::IntoHandlerResult,
>,
{
FilterBuilder {
state_factory: self.state_factory,
request_filter: self.request_filter,
response_filter: self.response_filter,
done_handler: self.done_handler,
websocket_upstream: websocket_upstream.into_handler(),
websocket_downstream: self.websocket_downstream,
}
}
pub fn on_upgrade_downstream<WsDownHnd, I>(
self,
websocket_downstream: WsDownHnd,
) -> FilterBuilder<Sf, Req, Res, Done, WsUp, WsDownHnd::Handler>
where
WsDownHnd: crate::handler::IntoHandler<
crate::extract::context::UpgradeDownstreamContext<Sf::State>,
I,
Output: crate::handler::IntoHandlerResult,
>,
{
FilterBuilder {
state_factory: self.state_factory,
request_filter: self.request_filter,
response_filter: self.response_filter,
done_handler: self.done_handler,
websocket_upstream: self.websocket_upstream,
websocket_downstream: websocket_downstream.into_handler(),
}
}
}
#[cfg(all(test, not(feature = "experimental_websocket")))]
mod builder_state_tests {
use super::*;
use crate::hl::flow::Flow;
#[test]
fn request_only_on_done_builds() {
let _filter = FilterBuilder::new()
.on_request(|| async { Flow::Continue(()) })
.on_done(|_s: ()| {})
.build();
}
#[test]
fn response_only_on_done_builds() {
let _filter = FilterBuilder::new()
.on_response(|| async {})
.on_done(|_s: ()| {})
.build();
}
#[test]
fn existing_forms_still_build() {
let _req = FilterBuilder::new()
.on_request(|| async { Flow::Continue(()) })
.build();
let _res = FilterBuilder::new().on_response(|| async {}).build();
let _dual = FilterBuilder::new()
.on_request(|| async { Flow::Continue(()) })
.on_response(|| async {})
.build();
}
#[test]
fn on_request_after_on_response_builds() {
let _filter = FilterBuilder::new()
.on_response(|| async {})
.on_request(|| async { Flow::Continue(()) })
.build();
}
}
#[cfg(all(test, feature = "experimental_websocket"))]
mod builder_state_tests {
use super::*;
use crate::hl::flow::Flow;
use crate::hl::state::State;
struct TestState;
#[test]
fn request_only_on_done_builds() {
let _filter = FilterBuilder::new()
.on_request(|| async { Flow::Continue(()) })
.on_done(|_s: ()| {})
.build();
}
#[test]
fn empty_both_on_done_reachable() {
let _builder = FilterBuilder::new().on_done(|_s: ()| {});
}
#[test]
fn response_only_on_done_builds() {
let _filter = FilterBuilder::new()
.on_response(|| async {})
.on_done(|_s: ()| {})
.build();
}
#[test]
fn response_only_build_retains_upgrade_handlers() {
let filter = FilterBuilder::new()
.on_response(|| async {})
.on_upgrade_upstream(|| async {})
.on_upgrade_downstream(|| async {})
.build();
fn assert_ws<Base, Sf, WsUp, WsDown>(_f: &WebSocketFilter<Base, Sf, WsUp, WsDown>) {}
assert_ws(&filter);
}
#[test]
fn existing_forms_still_build() {
let _req = FilterBuilder::new()
.on_request(|| async { Flow::Continue(()) })
.build();
let _dual = FilterBuilder::new()
.on_request(|| async { Flow::Continue(()) })
.on_response(|| async {})
.build();
}
#[test]
fn on_request_after_on_response_builds() {
let _filter = FilterBuilder::new()
.on_response(|| async {})
.on_request(|| async { Flow::Continue(()) })
.build();
}
macro_rules! bm_chain {
(Y, $rq:tt, $rs:tt, $up:tt, $dn:tt, $do:tt) => {
bm_chain!(@req (FilterBuilder::new().on_create(|| TestState)), $rq, $rs, $up, $dn, $do)
};
(N, $rq:tt, $rs:tt, $up:tt, $dn:tt, $do:tt) => {
bm_chain!(@req (FilterBuilder::new()), $rq, $rs, $up, $dn, $do)
};
(@req ($acc:expr), Y, $rs:tt, $up:tt, $dn:tt, $do:tt) => {
bm_chain!(@res ($acc.on_request(|| async { Flow::Continue(()) })), $rs, $up, $dn, $do)
};
(@req ($acc:expr), N, $rs:tt, $up:tt, $dn:tt, $do:tt) => {
bm_chain!(@res ($acc), $rs, $up, $dn, $do)
};
(@res ($acc:expr), Y, $up:tt, $dn:tt, $do:tt) => {
bm_chain!(@up ($acc.on_response(|| async {})), $up, $dn, $do)
};
(@res ($acc:expr), N, $up:tt, $dn:tt, $do:tt) => {
bm_chain!(@up ($acc), $up, $dn, $do)
};
(@up ($acc:expr), Y, $dn:tt, $do:tt) => {
bm_chain!(@down ($acc.on_upgrade_upstream(|| async {})), $dn, $do)
};
(@up ($acc:expr), N, $dn:tt, $do:tt) => {
bm_chain!(@down ($acc), $dn, $do)
};
(@down ($acc:expr), Y, $do:tt) => {
bm_chain!(@done ($acc.on_upgrade_downstream(|| async {})), $do)
};
(@down ($acc:expr), N, $do:tt) => {
bm_chain!(@done ($acc), $do)
};
(@done ($acc:expr), Y) => {
$acc.on_done(|_a| {}).build()
};
(@done ($acc:expr), N) => {
$acc.build()
};
}
macro_rules! builder_matrix {
($( $name:ident : $c:tt $rq:tt $rs:tt $up:tt $dn:tt $do:tt ; )*) => {
$(
#[test]
fn $name() {
let _f = bm_chain!($c, $rq, $rs, $up, $dn, $do);
}
)*
};
}
builder_matrix! {
res: N N Y N N N;
res_done: N N Y N N Y;
res_down: N N Y N Y N;
res_down_done: N N Y N Y Y;
res_up: N N Y Y N N;
res_up_done: N N Y Y N Y;
res_up_down: N N Y Y Y N;
res_up_down_done: N N Y Y Y Y;
req: N Y N N N N;
req_done: N Y N N N Y;
req_down: N Y N N Y N;
req_down_done: N Y N N Y Y;
req_up: N Y N Y N N;
req_up_done: N Y N Y N Y;
req_up_down: N Y N Y Y N;
req_up_down_done: N Y N Y Y Y;
req_res: N Y Y N N N;
req_res_done: N Y Y N N Y;
req_res_down: N Y Y N Y N;
req_res_down_done: N Y Y N Y Y;
req_res_up: N Y Y Y N N;
req_res_up_done: N Y Y Y N Y;
req_res_up_down: N Y Y Y Y N;
req_res_up_down_done: N Y Y Y Y Y;
create_res: Y N Y N N N;
create_res_done: Y N Y N N Y;
create_res_down: Y N Y N Y N;
create_res_down_done: Y N Y N Y Y;
create_res_up: Y N Y Y N N;
create_res_up_done: Y N Y Y N Y;
create_res_up_down: Y N Y Y Y N;
create_res_up_down_done: Y N Y Y Y Y;
create_req: Y Y N N N N;
create_req_done: Y Y N N N Y;
create_req_down: Y Y N N Y N;
create_req_down_done: Y Y N N Y Y;
create_req_up: Y Y N Y N N;
create_req_up_done: Y Y N Y N Y;
create_req_up_down: Y Y N Y Y N;
create_req_up_down_done: Y Y N Y Y Y;
create_req_res: Y Y Y N N N;
create_req_res_done: Y Y Y N N Y;
create_req_res_down: Y Y Y N Y N;
create_req_res_down_done: Y Y Y N Y Y;
create_req_res_up: Y Y Y Y N N;
create_req_res_up_done: Y Y Y Y N Y;
create_req_res_up_down: Y Y Y Y Y N;
create_req_res_up_down_done: Y Y Y Y Y Y;
}
#[test]
fn state_extracted_in_request_and_response() {
let _f = FilterBuilder::new()
.on_create(|| TestState)
.on_request(|State(_s): State<TestState>| async { Flow::Continue(()) })
.on_response(|State(_s): State<TestState>| async {})
.build();
}
#[test]
fn state_extracted_in_upgrade_handlers() {
let _f = FilterBuilder::new()
.on_create(|| TestState)
.on_response(|| async {})
.on_upgrade_upstream(|State(_s): State<TestState>| async {})
.on_upgrade_downstream(|State(_s): State<TestState>| async {})
.build();
}
}