use data; use handlebars_iron::{HandlebarsEngine, MemorySource, Template}; use iron::headers::ContentType; use iron::modifiers::Header; use iron::{self, Iron, Plugin, Chain, Request, Response, IronResult, status}; use rusqlite::Connection; use std::collections::BTreeMap; use persistent::Write; use handlebars_iron::handlebars::to_json; use serde_json::Map; use params; use error::LinoError; #[derive(Debug)] pub struct State { pub connection: Connection, } impl iron::typemap::Key for State { type Value = State; } fn get_param(r: &mut Request, param: &str) -> IronResult { let map = itry!(r.get_ref::()); match map.get(param) { Some(¶ms::Value::String(ref v)) => Ok(v.to_string()), _ => Err(From::from(LinoError::NotFound(param.to_string()))), } } fn make_renderer() -> HandlebarsEngine { let mut e = HandlebarsEngine::new(); let mut templates = BTreeMap::new(); templates.insert( "quotes".to_string(), include_str!("data/templates/quotes.hbs").to_string(), ); templates.insert( "add".to_string(), include_str!("data/templates/add.hbs").to_string(), ); templates.insert( "add_post".to_string(), include_str!("data/templates/add_post.hbs").to_string(), ); templates.insert( "approve".to_string(), include_str!("data/templates/approve.hbs").to_string(), ); templates.insert( "vote".to_string(), include_str!("data/templates/vote.hbs").to_string(), ); e.add(Box::new(MemorySource(templates))); if let Err(r) = e.reload() { panic!("Error loading templates: {}", r) } e } fn quotes(r: &mut Request) -> IronResult { let mut result = Map::new(); let quote_id = get_param(r, "id").ok().and_then( |id| id.parse::().ok(), ); let ordering = get_param(r, "order").unwrap_or("".to_string()); let quotes = { let mu = r.get::>().unwrap(); let state = mu.lock().unwrap(); match quote_id { Some(id) => vec![data::get_quote(&state.connection, id)?], None => data::get_quotes(&state.connection, &ordering)?, } }; result.insert("quotes".to_string(), to_json("es)); Ok(Response::with(( status::Ok, Header(ContentType::html()), Template::new("quotes", result), ))) } fn add_get(r: &mut Request) -> IronResult { Ok(Response::with(( status::Ok, Header(ContentType::html()), Template::new("add", Map::new()), ))) } fn add_post(r: &mut Request) -> IronResult { let nick = get_param(r, "nick")?; let date = get_param(r, "date")?; let quote = get_param(r, "quote")?; macro_rules! check { ($i:ident) => ( if $i.is_empty() { return Err(From::from(LinoError::BadRequest( format!("missing parameter: {}", stringify!($i))))); } ) } check!(nick); check!(date); check!(quote); { let mu = r.get::>().unwrap(); let state = mu.lock().unwrap(); data::new_quote(&state.connection, &date, &nick, "e)?; } Ok(Response::with(( status::Ok, Header(ContentType::html()), Template::new("add_post", Map::new()), ))) } fn approve(r: &mut Request) -> IronResult { let mut result = Map::new(); let quote_id = get_param(r, "id").ok().and_then( |id| id.parse::().ok(), ); let action = get_param(r, "action").unwrap_or("".to_string()); let quotes = { let mu = r.get::>().unwrap(); let state = mu.lock().unwrap(); if let Some(quote_id) = quote_id { info!("Approval for quote({}): {}", quote_id, action); if action == "approve" { data::approve_quote(&state.connection, quote_id)?; } else if action == "reject" { data::delete_quote(&state.connection, quote_id)?; } else { return Err(From::from( LinoError::BadRequest(format!("invalid action: {}", action)), )); } } data::get_pending_quotes(&state.connection)? }; result.insert("quotes".to_string(), to_json("es)); Ok(Response::with(( status::Ok, Header(ContentType::html()), Template::new("approve", result), ))) } pub fn vote(r: &mut Request) -> IronResult { let quote_id = get_param(r, "id").and_then(|id| { id.parse::().map_err(|e| { From::from(LinoError::BadRequest(format!("id: {}", e))) }) })?; let vote = get_param(r, "vote").and_then(|id| { id.parse::().map_err(|e| { From::from(LinoError::BadRequest(format!("vote: {}", e))) }) })?; if vote < 1 || vote > 5 { return Err(From::from(LinoError::BadRequest("bad vote".to_string()))); } let quote = { let mu = r.get::>().unwrap(); let state = mu.lock().unwrap(); data::new_vote(&state.connection, quote_id, vote); data::get_quote(&state.connection, quote_id)? }; let mut result = Map::new(); result.insert("quote".to_string(), to_json("e)); Ok(Response::with(( status::Ok, Header(ContentType::html()), Template::new("vote", result), ))) } pub fn serve(state: State, port: u16) { let router = router!( index: get "/" => quotes, add_get: get "/add.jsp" => add_get, add_post: post "/add.jsp" => add_post, quotes_jsp: get "/quotes.jsp" => quotes, view_quote: get "/view_quote" => quotes, approve: get "/approve.jsp" => approve, vote: get "/vote" => vote, ); let mut chain = Chain::new(router); chain.link_after(make_renderer()); chain.link(Write::::both(state)); let bind_address = format!("{}:{}", "::", port); let _server = Iron::new(chain).http(bind_address.as_str()); info!("Serving on {}", bind_address); }