use std::future::Future; use std::pin::Pin; use std::task::{self, Poll}; use futures::io::{AsyncRead, AsyncWrite}; use tower::Service; use super::pipeline::Connection; use super::Imap; use crate::proto::{Request, Response}; #[pin_project::pin_project] pub struct Connecting { pub conn: Connection, #[pin] pub state: ConnectingState, pub protocol: Imap, } #[pin_project::pin_project(project = ConnectingStateProj)] pub enum ConnectingState { Waiting { #[pin] service_fut: F, }, Ready { service: S, }, } impl Future for Connecting where C: AsyncRead + AsyncWrite + Unpin, F: Future>, ME: std::fmt::Display, S: Service, S::Error: std::fmt::Display, { type Output = (); fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll { use tokio_tower::pipeline::Server as PipelineServer; let mut this = self.project(); loop { let next = match this.state.as_mut().project() { ConnectingStateProj::Waiting { service_fut } => { let service = match futures::ready!(service_fut.poll(cx)) { Ok(service) => service, Err(err) => { tracing::error!("Connection error: {}", err); return Poll::Ready(()); } }; // TODO: Properly handle server greeting { use imap_codec::types::response::{Response, Status}; let status = match Status::ok(None, None, "Hello") { Ok(status) => status, Err(err) => { tracing::error!("Connection error: {}", err); return Poll::Ready(()); } }; let res = Response::Status(status); if let Err(err) = this.conn.send(res) { tracing::error!("Connection error: {}", err); return Poll::Ready(()); }; } ConnectingState::Ready { service } } ConnectingStateProj::Ready { service } => { let server = PipelineServer::new(this.conn, service); futures::pin_mut!(server); return server.poll(cx).map(|res| { if let Err(err) = res { tracing::debug!("Connection error: {}", err); } }); } }; this.state.set(next); } } }