use std::convert::Infallible;
use std::fmt;
use std::net::SocketAddr;
use actix_http::HttpService;
use actix_service::IntoService;
use puzz_core::response::IntoResponse;
use puzz_core::service::Service;
use puzz_core::{BoxError, Request};
use tokio::net::TcpStream;
mod compat;
struct ServerOptions {
workers: Option<usize>,
addr: Vec<SocketAddr>,
}
pub struct Server<F> {
factory: F,
options: Result<ServerOptions, BoxError>,
}
impl<F, S> Server<F>
where
F: Fn() -> S + Clone + Send + 'static,
S: Service<Request, Error = Infallible> + 'static,
S::Response: IntoResponse,
S::Future: 'static,
{
pub fn new(factory: F) -> Self {
Self {
factory,
options: Ok(ServerOptions {
workers: None,
addr: vec![],
}),
}
}
pub fn workers(mut self, num: usize) -> Self {
self.options = self.options.and_then(|mut options| {
options.workers = Some(num);
Ok(options)
});
self
}
pub fn bind<A>(mut self, addr: A) -> Self
where
A: Into<SocketAddr>,
{
self.options = self.options.and_then(|mut options| {
options.addr.push(addr.into());
Ok(options)
});
self
}
pub async fn run(self) -> Result<(), BoxError> {
let options = self.options?;
let factory = self.factory;
let factory = move || {
let service = compat::into_actix_service(factory());
let service = move |request: actix_http::Request| service.call(request);
async move { Ok::<_, Infallible>(service.into_service()) }
};
let mut server = actix_server::Server::build();
if let Some(workers) = options.workers {
server = server.workers(workers);
}
server
.bind("puzz", &options.addr[..], move || {
HttpService::<TcpStream, _, _, _, _>::build()
.finish(factory.clone())
.tcp()
})?
.run()
.await
.map_err(From::from)
}
}
impl<F> fmt::Debug for Server<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Server").finish()
}
}