forked from KokaKiwi/boitalettres
119 lines
3 KiB
Rust
119 lines
3 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::proto::{Request, Response};
|
|
use accept::Accept;
|
|
use pipeline::Connection;
|
|
pub use service::MakeServiceRef;
|
|
|
|
pub mod accept;
|
|
mod conn;
|
|
mod pipeline;
|
|
mod service;
|
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
pub enum Error<A> {
|
|
#[error("Error occured when accepting new connections")]
|
|
Accept(#[source] A),
|
|
#[error("Error occured on service creation")]
|
|
MakeService(#[source] tower::BoxError),
|
|
}
|
|
|
|
#[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,
|
|
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,
|
|
{
|
|
type Output = Result<(), Error<I::Error>>;
|
|
|
|
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(Into::into)
|
|
.map_err(Error::MakeService)?;
|
|
|
|
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,
|
|
}
|
|
}
|
|
}
|