use crate::{connection::Connection, enums::CallbackResult, error::Error};
use core::task::Poll;
use pin_project_lite::pin_project;
use std::pin::Pin;
pub trait ConnectionFuture: 'static + Send + Sync {
fn poll(
self: Pin<&mut Self>,
connection: &mut Connection,
ctx: &mut core::task::Context,
) -> Poll<Result<(), Error>>;
}
pub(crate) type ConnectionFutureResult = Result<Option<Pin<Box<dyn ConnectionFuture>>>, Error>;
pub(crate) struct ErrorFuture {
error: Option<Error>,
}
impl ConnectionFuture for ErrorFuture {
fn poll(
mut self: Pin<&mut Self>,
_connection: &mut Connection,
_ctx: &mut core::task::Context,
) -> Poll<Result<(), Error>> {
let err = self.error.take().expect(
"ErrorFuture should be initialized with Some(error) and a Future should never
be polled after it returns Poll::Ready",
);
Poll::Ready(Err(err))
}
}
pin_project! {
struct OptionalFuture {
option: Option<Pin<Box<dyn ConnectionFuture>>>,
}
}
impl OptionalFuture {
fn new(input: ConnectionFutureResult) -> Self {
match input {
Ok(option) => OptionalFuture { option },
Err(error) => {
let error = Some(error);
OptionalFuture {
option: Some(Box::pin(ErrorFuture { error })),
}
}
}
}
}
impl ConnectionFuture for OptionalFuture {
fn poll(
mut self: Pin<&mut Self>,
conn: &mut Connection,
ctx: &mut core::task::Context,
) -> Poll<Result<(), Error>> {
match self.option.as_mut() {
Some(future) => future.as_mut().poll(conn, ctx),
None => Poll::Ready(Ok(())),
}
}
}
#[non_exhaustive]
#[derive(PartialEq)]
enum MarkDone {
ClientHello,
None,
}
pin_project! {
pub(crate) struct AsyncCallback {
#[pin]
future: OptionalFuture,
cleanup: MarkDone,
}
}
impl AsyncCallback {
pub(crate) fn poll(
self: Pin<&mut Self>,
conn: &mut Connection,
ctx: &mut core::task::Context,
) -> Poll<Result<(), Error>> {
let this = self.project();
let poll = this.future.poll(conn, ctx);
if let Poll::Ready(Ok(())) = poll {
if this.cleanup == &MarkDone::ClientHello {
conn.mark_client_hello_cb_done()?;
}
}
poll
}
pub(crate) fn trigger_client_hello_cb(
future: ConnectionFutureResult,
conn: &mut Connection,
) -> CallbackResult {
let future = OptionalFuture::new(future);
let cleanup = MarkDone::ClientHello;
let callback = AsyncCallback { future, cleanup };
conn.set_async_callback(callback);
CallbackResult::Success
}
pub(crate) fn trigger(future: ConnectionFutureResult, conn: &mut Connection) -> CallbackResult {
let future = OptionalFuture::new(future);
let cleanup = MarkDone::None;
let callback = AsyncCallback { future, cleanup };
conn.set_async_callback(callback);
CallbackResult::Success
}
}