use std::future::Future; use std::pin::Pin; use std::task::{self, Poll}; use futures::io::{AsyncRead, AsyncWrite}; use imap_codec::types::response::Capability; use crate::errors::{Error, Result}; use crate::proto::{Request, Response}; use accept::Accept; use pipeline::Connection; pub use service::MakeServiceRef; pub mod accept; mod conn; mod pipeline; mod service; #[derive(Debug, Default, Clone)] pub struct Imap { pub capabilities: Vec, } #[pin_project::pin_project] pub struct Server { #[pin] incoming: I, make_service: S, protocol: Imap, } impl Server where I: Accept, { #[allow(clippy::new_ret_no_self)] pub fn new(incoming: I) -> Builder { Builder::new(incoming) } } impl Future for Server where I: Accept, I::Conn: AsyncRead + AsyncWrite + Unpin + Send + 'static, I::Error: Send + Sync + 'static, S: MakeServiceRef, S::MakeError: Into + std::fmt::Display, S::Error: std::fmt::Display, S::Future: Send + 'static, S::Service: Send + 'static, >::Future: Send + 'static, { type Output = Result<()>; fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll { loop { let this = self.as_mut().project(); if let Some(conn) = futures::ready!(this.incoming.poll_accept(cx)) { let conn = conn.map_err(Error::accept)?; futures::ready!(this.make_service.poll_ready_ref(cx)) .map_err(Error::make_service)?; let service_fut = this.make_service.make_service_ref(&conn); tokio::task::spawn(conn::Connecting { conn: Connection::new(conn), state: conn::ConnectingState::Waiting { service_fut }, protocol: this.protocol.clone(), }); } else { return Poll::Ready(Ok(())); } } } } pub struct Builder { incoming: I, protocol: Imap, } #[allow(clippy::needless_update)] impl Builder { pub fn new(incoming: I) -> Self { Self { incoming, protocol: Default::default(), } } pub fn capabilities(self, capabilities: impl IntoIterator) -> Self { let protocol = Imap { capabilities: capabilities.into_iter().collect(), ..self.protocol }; Self { protocol, ..self } } pub fn serve(self, make_service: S) -> Server where S: MakeServiceRef, { Server { incoming: self.incoming, make_service, protocol: self.protocol, } } }