1use std::convert::Infallible;
2use std::fmt;
3use std::net::SocketAddr;
4
5use actix_http::HttpService;
6use actix_service::IntoService;
7use puzz_core::response::IntoResponse;
8use puzz_core::service::Service;
9use puzz_core::{BoxError, Request};
10use tokio::net::TcpStream;
11
12mod compat;
13
14struct ServerOptions {
15 workers: Option<usize>,
16 addr: Vec<SocketAddr>,
17}
18
19pub struct Server<F> {
34 factory: F,
35 options: Result<ServerOptions, BoxError>,
36}
37
38impl<F, S> Server<F>
39where
40 F: Fn() -> S + Clone + Send + 'static,
41 S: Service<Request, Error = Infallible> + 'static,
42 S::Response: IntoResponse,
43 S::Future: 'static,
44{
45 pub fn new(factory: F) -> Self {
47 Self {
48 factory,
49 options: Ok(ServerOptions {
50 workers: None,
51 addr: vec![],
52 }),
53 }
54 }
55
56 pub fn workers(mut self, num: usize) -> Self {
60 self.options = self.options.and_then(|mut options| {
61 options.workers = Some(num);
62 Ok(options)
63 });
64 self
65 }
66
67 pub fn bind<A>(mut self, addr: A) -> Self
69 where
70 A: Into<SocketAddr>,
71 {
72 self.options = self.options.and_then(|mut options| {
73 options.addr.push(addr.into());
74 Ok(options)
75 });
76 self
77 }
78
79 pub async fn run(self) -> Result<(), BoxError> {
81 let options = self.options?;
82 let factory = self.factory;
83
84 let factory = move || {
85 let service = compat::into_actix_service(factory());
86 let service = move |request: actix_http::Request| service.call(request);
87
88 async move { Ok::<_, Infallible>(service.into_service()) }
89 };
90
91 let mut server = actix_server::Server::build();
92
93 if let Some(workers) = options.workers {
94 server = server.workers(workers);
95 }
96
97 server
98 .bind("puzz", &options.addr[..], move || {
99 HttpService::<TcpStream, _, _, _, _>::build()
100 .finish(factory.clone())
101 .tcp()
102 })?
103 .run()
104 .await
105 .map_err(From::from)
106 }
107}
108
109impl<F> fmt::Debug for Server<F> {
110 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111 f.debug_struct("Server").finish()
112 }
113}