diff --git a/src/app.rs b/src/app.rs
new file mode 100644
index 0000000..b2e6079
--- /dev/null
+++ b/src/app.rs
@@ -0,0 +1,92 @@
+use std::{convert::Infallible, net::SocketAddr};
+
+use http::{Method, Request, Response, StatusCode};
+use hyper::{server::conn::Http, service::service_fn, Body};
+use tokio::net::TcpListener;
+
+use crate::{handler::Handler, router::Router, Error, ServerError};
+
+pub struct App {
+ router: Router,
+}
+
+impl App {
+ pub fn new() -> Self {
+ Self {
+ router: Router::new(),
+ }
+ }
+
+ pub fn get(&mut self, path: &str, ch: Handler) {
+ self.router.add(Method::GET, path.to_string(), ch);
+ }
+
+ pub fn post(&mut self, path: &str, ch: Handler) {
+ self.router.add(Method::POST, path.to_string(), ch);
+ }
+
+ pub fn delete(&mut self, path: &str, ch: Handler) {
+ self.router.add(Method::DELETE, path.to_string(), ch);
+ }
+
+ pub fn put(&mut self, path: &str, ch: Handler) {
+ self.router.add(Method::PUT, path.to_string(), ch);
+ }
+
+ pub fn options(&mut self, path: &str, ch: Handler) {
+ self.router.add(Method::OPTIONS, path.to_string(), ch);
+ }
+
+ pub fn patch(&mut self, path: &str, ch: Handler) {
+ self.router.add(Method::PATCH, path.to_string(), ch);
+ }
+
+ pub fn head(&mut self, path: &str, ch: Handler) {
+ self.router.add(Method::HEAD, path.to_string(), ch);
+ }
+
+ pub fn connect(&mut self, path: &str, ch: Handler) {
+ self.router.add(Method::CONNECT, path.to_string(), ch);
+ }
+
+ pub fn trace(&mut self, path: &str, ch: Handler) {
+ self.router.add(Method::TRACE, path.to_string(), ch);
+ }
+
+ pub async fn dispatch(&self, req: Request
) -> Result, Infallible> {
+ match self.router.dispatch(req).await {
+ Ok(resp) => Ok(resp),
+ Err(e) => match e {
+ Error::StatusCode(sc) => Ok(Response::builder()
+ .status(sc)
+ .body(Body::default())
+ .unwrap()),
+ Error::InternalServerError(_) => Ok(Response::builder()
+ .status(StatusCode::INTERNAL_SERVER_ERROR)
+ .body(Body::default())
+ .unwrap()),
+ },
+ }
+ }
+
+ pub async fn serve(&'static self, addr: String) -> Result<(), ServerError> {
+ let socketaddr: SocketAddr = addr.parse()?;
+
+ let s = self.clone();
+ let sfn = service_fn(move |req: Request| s.dispatch(req));
+
+ let tcp_listener = TcpListener::bind(socketaddr).await?;
+ loop {
+ let (tcp_stream, _) = tcp_listener.accept().await?;
+ tokio::task::spawn(async move {
+ if let Err(http_err) = Http::new()
+ .http1_keep_alive(true)
+ .serve_connection(tcp_stream, sfn)
+ .await
+ {
+ eprintln!("Error while serving HTTP connection: {}", http_err);
+ }
+ });
+ }
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 899c5d8..0099800 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,15 +1,25 @@
+pub mod app;
pub mod handler;
pub mod macros;
pub mod path;
pub mod router;
-use handler::Handler;
use http::{Request, Response};
-
-use std::{collections::BTreeMap, pin::Pin};
+use std::pin::Pin;
pub(crate) type PinBox = Pin>;
+pub struct ServerError(String);
+
+impl From for ServerError
+where
+ T: ToString,
+{
+ fn from(t: T) -> Self {
+ ServerError(t.to_string())
+ }
+}
+
#[derive(Clone, Debug)]
pub enum Error {
StatusCode(http::StatusCode),
@@ -42,8 +52,3 @@ impl From for Error {
}
pub type HTTPResult = Result<(Request, Option>), Error>;
-
-pub struct App {
- #[allow(dead_code)] // FIXME remove
- routes: BTreeMap,
-}
diff --git a/src/router.rs b/src/router.rs
index d56aa02..4fe4f6c 100644
--- a/src/router.rs
+++ b/src/router.rs
@@ -34,7 +34,6 @@ impl Ord for Route {
}
impl Route {
- #[allow(dead_code)]
fn new(method: http::Method, path: String, handler: Handler) -> Self {
Self {
method,
@@ -63,7 +62,6 @@ impl Router {
Self(Vec::new())
}
- #[allow(dead_code)]
pub(crate) fn add(&mut self, method: http::Method, path: String, ch: Handler) -> Self {
self.0.push(Route::new(method, path, ch));
self.clone()