use hyper::{self, Server, Request, Response, Body, Method};
use hyper::service::service_fn;
use futures::future::Future;
use futures_cpupool::CpuPool;
use queen_log::color::Print;
pub use self::route::Route;
pub use self::group::Group;
use self::middleware::Middleware;
use self::context::Context;
use crate::error::Result;
#[macro_use]
mod macros;
mod route;
mod group;
pub mod middleware;
pub mod context;
pub type Handle = Fn(&mut Context) + Send + Sync + 'static;
#[derive(Default)]
pub struct App {
groups: Vec<Group>,
begin: Vec<Middleware>,
before: Vec<Middleware>,
after: Vec<Middleware>,
finish: Vec<Middleware>,
not_found: Option<Middleware>
}
impl App {
pub fn new() -> App {
App {
groups: vec![Group::new("")],
begin: Vec::new(),
before: Vec::new(),
after: Vec::new(),
finish: Vec::new(),
not_found: None
}
}
pub fn add<H>(&mut self, method: Method, pattern: &str, handle: H) -> &mut Route
where H: Fn(&mut Context) + Send + Sync + 'static
{
self.groups.get_mut(0).unwrap().add(method, pattern, handle)
}
route!(
get
);
route!(
put
);
route!(
post
);
route!(
head
);
route!(
patch
);
route!(
trace
);
route!(
delete
);
route!(
options
);
route!(
connect
);
pub fn mount<F>(&mut self, prefix: &str, func: F) -> &mut App
where F: Fn(&mut Group)
{
let mut group = Group::new(prefix);
func(&mut group);
self.groups.push(group);
self
}
pub fn mount_group(&mut self, group: Group) -> &mut App {
self.groups.push(group);
self
}
middleware!(
begin
);
middleware!(
before
);
middleware!(
after
);
middleware!(
finish
);
pub fn middleware<F>(&mut self, func: F) -> &mut App
where F: Fn(&mut App)
{
func(self);
self
}
pub fn not_found<H>(&mut self, handle: H)
where H: Fn(&mut Context) + Send + Sync + 'static
{
self.not_found = Some(Middleware {
inner: Box::new(handle),
});
}
fn handle(&self, request: Request<Body>) -> Response<Body> {
let mut context = Context::new(self, request);
let mut route_found = false;
for begin in self.begin.iter() {
begin.execute_always(&mut context);
}
if context.next() {
let path = {
let path = context.request.uri().path();
if path != "/" {
path.trim_end_matches('/').to_owned()
} else {
path.to_owned()
}
};
'outer: for group in self.groups.iter() {
if let Some(routes) = group.routes.get(context.request.method()) {
for route in routes.iter() {
if let Some(ref regex) = route.regex {
let caps = regex.captures(&path);
if let Some(caps) = caps {
route_found = true;
let matches = route.path();
for (key, value) in matches.iter() {
context.request.params().insert(key.to_owned(), caps.get(*value).unwrap().as_str().to_owned());
}
}
} else {
let pattern = {
let pattern = route.pattern();
if pattern != "/" {
pattern.trim_end_matches('/').to_owned()
} else {
pattern.to_owned()
}
};
if pattern == path {
route_found = true;
}
}
if route_found {
for before in self.before.iter() {
before.execute(&mut context);
}
for before in group.before.iter() {
before.execute(&mut context);
}
route.execute(&mut context);
for after in group.after.iter() {
after.execute(&mut context);
}
for after in self.after.iter() {
after.execute(&mut context);
}
break 'outer;
}
}
}
}
if !route_found {
if let Some(ref not_found) = self.not_found {
not_found.execute(&mut context);
} else {
context.response.status_code(404).from_text("Not Found").unwrap();
}
}
}
for finish in self.finish.iter() {
finish.execute_always(&mut context);
}
context.finish()
}
pub fn run(&self, addr: &str, thread_size: usize) -> Result<()> {
type BoxFut = Box<Future<Item = Response<Body>, Error = hyper::Error> + Send>;
let app = unsafe {
let a: *const App = &*self;
&*a
};
let sincere_logo = Print::green(
r"
__.._.. . __ .___.__ .___
(__ | |\ |/ `[__ [__)[__
.__)_|_| \|\__.[___| \[___
"
);
println!("{}", sincere_logo);
println!(
" {}{} {} {} {}",
Print::green("Server running at http://"),
Print::green(addr),
Print::green("on"),
Print::green(thread_size),
Print::green("threads.")
);
let addr = addr.parse().expect("Address is not valid");
let thread_pool = CpuPool::new(thread_size);
let new_svc = move || {
let pool = thread_pool.clone();
service_fn(move |req| -> BoxFut {
let rep = pool.spawn_fn(move || {
let response = app.handle(req);
Ok(response)
});
Box::new(rep)
})
};
let server = Server::bind(&addr).serve(new_svc).map_err(|e| eprintln!("server error: {}", e));
hyper::rt::run(server);
Ok(())
}
}