#![feature(slice_patterns)]
extern crate futures;
extern crate hyper;
extern crate rusqlite;
extern crate time;
extern crate mustache;
extern crate rustc_serialize;
extern crate tokio_core;
extern crate tokio_io;
extern crate unix_socket;
#[macro_use]
extern crate lazy_static;
pub mod errors;
pub mod model;
pub mod template;
#[macro_use]
pub mod util;
use futures::future::Future;
use hyper::Method;
use hyper::header::{ContentLength,ContentType};
use hyper::server::{Http, Request, Response, Service};
use std::io::Read;
struct CG {
db: rusqlite::Connection,
}
impl Service for CG {
type Request = Request;
type Response = Response;
type Error = hyper::Error;
type Future = Box<Future<Item=Self::Response, Error=Self::Error>>;
fn call(&self, req: Request) -> Self::Future {
util::log_request(&req);
match self.route(req) {
Ok(pg) =>
CG::respond(pg),
Err(err) => {
error!("Found error: {:?}", err);
CG::respond_404(format!("error: {:?}", err))
}
}
}
}
enum Page {
HTML(String),
PNG(Vec<u8>),
StaticPNG(&'static [u8]),
}
impl CG {
fn main() -> Result<(), errors::CGError> {
match std::env::var("CG_UNIX_SOCK") {
Ok(addr) => {
std::fs::remove_file(&addr)?;
let mut socket = unix_socket::UnixListener::bind(addr)?;
let mut core = tokio_core::reactor::Core::new()?;
let http: Http = Http::new();
loop {
let db = rusqlite::Connection::open("cocktails.db")?;
core.run(
http.serve_connection(
tokio_io::io::AllowStdIo::new(socket.accept()?.0),
CG { db },
)
)?;
}
}
Err(_) => {
let addr = "127.0.0.1:3000".parse()?;
let server = Http::new().bind(&addr, || {
let db = rusqlite::Connection::open("cocktails.db").unwrap();
Ok(CG { db })
})?;
println!("serving on {:?}", addr);
Ok(server.run()?)
}
}
}
fn route(&self, req: Request) -> Result<Page, errors::RuntimeError> {
let path: Vec<&str> = req.path().split("/").skip(1).collect();
Ok(match (req.method(), path.as_slice()) {
(&Method::Get, &[""]) => {
let ck = model::get_cocktail_list(&self.db)?;
Page::HTML(template::render_cocktails(ck)?)
}
(&Method::Get, &["favicon.ico"]) =>
Page::HTML("icon".to_owned()),
(&Method::Get, &["cocktail", name]) =>
Page::HTML(format!("cocktail {}", name)),
(&Method::Get, &["tagged", tag]) =>
Page::HTML(format!("tag {}", tag)),
(&Method::Get, &["static", "header-logo.png"]) =>
Page::StaticPNG(template::HEADER_IMAGE),
(&Method::Get, &["imgs", filename]) => {
let mut img_path = std::path::PathBuf::from("imgs");
img_path.push(filename);
let mut f = std::fs::File::open(img_path.as_path())?;
let mut vec = Vec::new();
let _ = f.read_to_end(&mut vec)?;
Page::PNG(vec)
}
(_, _) => {
return Err(errors::RuntimeError::UnknownPath(
req.path().to_owned()));
}
})
}
fn respond(p: Page) -> <CG as Service>::Future {
match p {
Page::HTML(s) =>
Box::new(futures::future::ok(
Response::new()
.with_header(ContentLength(s.len() as u64))
.with_header(ContentType::html())
.with_body(s))),
Page::PNG(s) =>
Box::new(futures::future::ok(
Response::new()
.with_header(ContentLength(s.len() as u64))
.with_header(ContentType::png())
.with_body(s))),
Page::StaticPNG(s) =>
Box::new(futures::future::ok(
Response::new()
.with_header(ContentLength(s.len() as u64))
.with_header(ContentType::png())
.with_body(s))),
}
}
fn respond_404(s: String) -> <CG as Service>::Future {
Box::new(futures::future::ok(
Response::new()
.with_status(hyper::StatusCode::NotFound)
.with_header(ContentLength(s.len() as u64))
.with_body(s)))
}
}
fn main() {
if let Err(err) = CG::main() {
err.pretty();
std::process::exit(99);
}
}