use std::convert::TryInto; use gotham::{ helpers::http::response::create_response, hyper::{Body, Response}, state::{FromState, State}, }; use http::status::StatusCode; use mime::TEXT_HTML; use typed_html::{html, text, dom::DOMTree, types::{Class, SpacedSet}}; use diesel::prelude::*; use chrono::{offset::Local, Datelike, Duration, NaiveDate}; use libticker::{ schema::{self, events::dsl::events}, model::Event, }; use crate::AppState; 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 } 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 limit = Local::today().naive_local().and_hms(0, 0, 0) + Duration::weeks(2); let es = events .filter(schema::events::dtstart.ge(&today)) .filter(schema::events::dtstart.lt(&limit)) .order_by(schema::events::dtstart.asc()) .then_order_by(schema::events::dtend.desc()) .load::(&*db) .unwrap(); let days = group_by_day(&es); let config = &app_state.config; let doc: DOMTree = html!( "Ticker" { days.iter().map(|day| { let mut day_class: SpacedSet = ["date"].try_into().unwrap(); day_class.add(&format!("wd{}", day.date.weekday().num_days_from_monday())[..]); html!(

{ text!("{}", day.date.day()) } { text!("{}", &config.months[day.date.month0() as usize]) } { text!("{}", &config.weekdays[day.date.weekday().num_days_from_monday() as usize]) }

{ day.events.iter().map(|e| html!(

{ text!("{}", &e.dtstart.format("%H:%S")) }

{ match &e.url { None => html!(

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

), Some(url) => html!(

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

), } }

{ text!("{}", e.location.as_ref().unwrap_or(&"".to_owned())) }

)) }
) }) } ); format!("\n{}", doc.to_string()) } pub 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) }