use crate::reactor::http::HttpReactor;
use crate::{
event::{
Event, EventKind, Exchange, ExchangeComplete, RequestBody, RequestHeaders, RequestTrailers,
ResponseBody, ResponseHeaders, ResponseTrailers, Start,
},
BoxFuture,
};
pub trait ExchangeEvent: Event {
fn into_dynamic_exchange(exchange: Exchange<Self>) -> DynamicExchange;
fn get(dynamic_exchange: &DynamicExchange) -> Option<&Exchange<Self>>;
fn insert(dynamic_exchange: &mut DynamicExchange, exchange: Exchange<Self>) -> &Exchange<Self>;
fn wait_for(
dynamic_exchange: &mut DynamicExchange,
buffering: bool,
) -> BoxFuture<Option<Exchange<Self>>>;
}
macro_rules! impl_exchange_event {
() => {};
($event:ident $(,$before:ident)*) => {
impl ExchangeEvent for $event {
fn into_dynamic_exchange(exchange: Exchange<Self>) -> DynamicExchange {
DynamicExchange::$event(exchange)
}
fn get(dynamic_exchange: &DynamicExchange) -> Option<&Exchange<Self>> {
if let DynamicExchange::$event(e) = dynamic_exchange {
Some(e)
} else {
None
}
}
fn insert(dynamic_exchange: &mut DynamicExchange, exchange: Exchange<Self>) -> &Exchange<Self> {
*dynamic_exchange = DynamicExchange::$event(exchange);
if let DynamicExchange::$event(e) = dynamic_exchange {
e
} else {
unreachable!()
}
}
#[allow(unused)]
fn wait_for(dynamic_exchange: &mut DynamicExchange, buffering: bool) -> BoxFuture<Option<Exchange<Self>>> {
Box::pin(async move {
let mut current = DynamicExchange::Taken;
std::mem::swap(dynamic_exchange, &mut current);
match current {
$(
DynamicExchange::$before(e) => return Some(e.wait_for_event_buffering::<Self>(buffering).await),
)*
DynamicExchange::$event(e) => return Some(e),
_ => {}
}
std::mem::swap(dynamic_exchange, &mut current);
None
})
}
}
impl_exchange_event!($($before),*);
};
}
macro_rules! exchange_event {
([] $($reversed:tt)*) => {
impl_exchange_event!($($reversed),*); };
([$first:tt $($rest:tt)*] $($reversed:tt)*) => {
exchange_event!([$($rest)*] $first $($reversed)*); };
}
macro_rules! send_response {
(request, $event:ident, $this:expr, $exchange:expr, $status_code:expr, $headers:expr, $body:expr) => {{
$exchange.send_response($status_code, $headers, $body);
true
}};
(response_headers, $event:ident, $this:expr, $exchange:expr, $status_code:expr, $headers:expr, $body:expr) => {{
$exchange.send_response($status_code, $headers, $body);
true
}};
($dir:ident, $event:ident, $this:expr, $exchange:expr, $status_code:expr, $headers:expr, $body:expr) => {{
*($this) = DynamicExchange::$event($exchange);
false
}};
}
macro_rules! dynamic_exchange_impl {
($($dir:ident $event:ident)*) => {
pub enum DynamicExchange {
Taken,
Consumed,
$($event(Exchange<$event>),)*
}
impl DynamicExchange {
pub fn current_event(&self) -> Option<EventKind> {
match self {
Self::Taken => unreachable!(),
Self::Consumed => None,
$(
Self::$event(_) => Some($event::kind()),
)*
}
}
pub fn get_reactor(&self) -> Option<&HttpReactor> {
match self {
Self::Taken => unreachable!(),
Self::Consumed => None,
$(
Self::$event(e) => Some(&e.reactor),
)*
}
}
pub fn send_response(
&mut self,
status_code: u32,
headers: Vec<(&str, &str)>,
body: Option<&[u8]>
) -> bool {
let mut current = DynamicExchange::Consumed;
std::mem::swap(self, &mut current);
match current {
Self::Taken => unreachable!(),
Self::Consumed => false,
$(
Self::$event(exchange) =>
send_response!(
$dir,
$event,
self,
exchange,
status_code,
headers,
body
),
)*
}
}
}
exchange_event!([$($event)*]);
};
}
dynamic_exchange_impl! {
start Start
request RequestHeaders
request RequestBody
request RequestTrailers
response_headers ResponseHeaders
response ResponseBody
response ResponseTrailers
done ExchangeComplete
}
impl DynamicExchange {
pub fn new<E: ExchangeEvent>(exchange: Exchange<E>) -> Self {
E::into_dynamic_exchange(exchange)
}
pub fn get<E: ExchangeEvent>(&self) -> Option<&Exchange<E>> {
E::get(self)
}
pub async fn wait_for_event<'a, E: ExchangeEvent + 'a>(
&'a mut self,
) -> Option<&'a Exchange<E>> {
self.wait_for_event_buffering(true).await
}
pub async fn wait_for_event_buffering<'a, E: ExchangeEvent + 'a>(
&'a mut self,
buffering: bool,
) -> Option<&'a Exchange<E>> {
E::wait_for(self, buffering)
.await
.map(move |e| E::insert(self, e))
}
}