use std::future::Future; use std::pin::Pin; use std::task::{self, Poll}; use futures::io::{AsyncRead, AsyncWrite}; use imap_codec::types::core::Tag; 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::Future: Send + 'static, 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 futures::SinkExt; let greeting = Response::ok("Hello").unwrap(); // "Hello" is a valid // greeting this.conn.start_send_unpin((None, greeting)).unwrap(); } ConnectingState::Ready { service } } ConnectingStateProj::Ready { service } => { let server = PipelineServer::new(this.conn, PipelineService { inner: 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); } } } struct PipelineService { inner: S, } impl Service for PipelineService where S: Service, S::Future: Send + 'static, { type Response = (Option, S::Response); type Error = S::Error; type Future = futures::future::BoxFuture<'static, Result>; fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll> { self.inner.poll_ready(cx) } fn call(&mut self, req: Request) -> Self::Future { use futures::{FutureExt, TryFutureExt}; let tag = req.tag.clone(); self.inner.call(req).map_ok(|res| (Some(tag), res)).boxed() } }