boitalettres/src/server/mod.rs

114 lines
2.8 KiB
Rust

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<Capability>,
}
#[pin_project::pin_project]
pub struct Server<I, S> {
#[pin]
incoming: I,
make_service: S,
protocol: Imap,
}
impl<I> Server<I, ()>
where
I: Accept,
{
#[allow(clippy::new_ret_no_self)]
pub fn new(incoming: I) -> Builder<I> {
Builder::new(incoming)
}
}
impl<I, S> Future for Server<I, S>
where
I: Accept,
I::Conn: AsyncRead + AsyncWrite + Unpin + Send + 'static,
I::Error: Send + Sync + 'static,
S: MakeServiceRef<I::Conn, Request, Response = Response>,
S::MakeError: Into<tower::BoxError> + std::fmt::Display,
S::Error: std::fmt::Display,
S::Future: Send + 'static,
S::Service: Send + 'static,
<S::Service as tower::Service<Request>>::Future: Send + 'static,
{
type Output = Result<()>;
fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
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<I: Accept> {
incoming: I,
protocol: Imap,
}
#[allow(clippy::needless_update)]
impl<I: Accept> Builder<I> {
pub fn new(incoming: I) -> Self {
Self {
incoming,
protocol: Default::default(),
}
}
pub fn capabilities(self, capabilities: impl IntoIterator<Item = Capability>) -> Self {
let protocol = Imap {
capabilities: capabilities.into_iter().collect(),
..self.protocol
};
Self { protocol, ..self }
}
pub fn serve<S>(self, make_service: S) -> Server<I, S>
where
S: MakeServiceRef<I::Conn, Request, Response = Response>,
{
Server {
incoming: self.incoming,
make_service,
protocol: self.protocol,
}
}
}