#![recursion_limit="1024"] #[macro_use] extern crate gotham_derive; use std::sync::{Arc, Mutex}; use gotham::{ handler::assets::FileOptions, helpers::http::response::create_response, hyper::{Body, Response}, router::builder::{DefineSingleRoute, DrawRoutes}, middleware::state::StateMiddleware, pipeline::single::single_pipeline, pipeline::single_middleware, router::builder::*, state::{FromState, State}, }; use http::status::StatusCode; use mime::TEXT_HTML; use typed_html::{html, text, dom::DOMTree}; use diesel::{Connection, pg::PgConnection, prelude::*}; use chrono::{offset::Local, NaiveDate}; use libticker::{ config::{Config, CalendarOptions}, schema::{self, events::dsl::events}, model::{Calendar, Event}, ics::{Object, Timestamp, GetValue}, }; fn fix_url(s: &str) -> std::borrow::Cow { if s.starts_with("http:") || s.starts_with("https:") { s.into() } else { format!("http://{}", s).into() } } struct DayEvents<'e> { date: NaiveDate, events: &'e [Event], } /// assumes pre-sorted input fn group_by_day(es: &[Event]) -> Vec { let mut results = vec![]; let mut prev_date = None; let mut date_start = 0; for (i, event) in es.iter().enumerate() { if prev_date.is_some() && prev_date != Some(event.dtstart.date()) { if i > date_start { results.push(DayEvents { date: prev_date.unwrap().clone(), events: &es[date_start..i], }); date_start = i; } } prev_date = Some(event.dtstart.date()); } results } #[derive(Clone, StateData)] struct AppState { db: Arc>, } fn render_index(app_state: &AppState) -> String { let db = app_state.db.lock().unwrap(); let today = Local::today().naive_local().and_hms(0, 0, 0); let es = events .filter(schema::events::dtstart.ge(&today)) .order_by(schema::events::dtstart.asc()) .then_order_by(schema::events::dtend.desc()) .load::(&*db) .unwrap(); let days = group_by_day(&es); let doc: DOMTree = html!( "Ticker"

"Ticker"

{ days.iter().map(|day| html!(
{ day.events.iter().map(|e| html!(
{ match &e.url { None => html!(

{ text!("{}", &e.summary) }

), Some(url) => html!(

{ text!("{}", &e.summary) }

), } }

{ text!("{}", &e.dtstart) }

{ e.location.as_ref().map(|location| html!(

{ text!("{}", location) }

)) }
)) }
)) } ); format!("\n{}", doc.to_string()) } fn index(state: State) -> (State, Response) { let message = { let app_state = AppState::borrow_from(&state); render_index(app_state) }; let res = create_response(&state, StatusCode::OK, TEXT_HTML, message); (state, res) } fn main() { let config = Config::read_yaml_file("../config.yaml"); let db = PgConnection::establish(&config.db_url) .expect("DB"); let state = AppState { db: Arc::new(Mutex::new(db)) }; let (chain, pipelines) = single_pipeline( single_middleware( StateMiddleware::new(state) ) ); let router = build_router(chain, pipelines, |route| { route.get("/").to(index); route.get("static/*").to_dir( FileOptions::new(&"static") // TODO: .with_cache_control("no-cache") .with_gzip(true) .build() ); }); gotham::start("[::1]:8400", router) }