diff --git a/ticker-serve/src/index.rs b/ticker-serve/src/index.rs new file mode 100644 index 0000000..7ff8ba3 --- /dev/null +++ b/ticker-serve/src/index.rs @@ -0,0 +1,113 @@ +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}; +use diesel::prelude::*; +use chrono::{offset::Local, 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 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" + + + + { 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()) +} + +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) +} diff --git a/ticker-serve/src/main.rs b/ticker-serve/src/main.rs index 2903f0a..b1d4327 100644 --- a/ticker-serve/src/main.rs +++ b/ticker-serve/src/main.rs @@ -6,129 +6,20 @@ 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 diesel::{Connection, pg::PgConnection}; -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 -} +use libticker::config::Config; +mod index; #[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) +pub struct AppState { + pub db: Arc>, } fn main() { @@ -145,7 +36,7 @@ fn main() { ) ); let router = build_router(chain, pipelines, |route| { - route.get("/").to(index); + route.get("/").to(index::index); route.get("static/*").to_dir( FileOptions::new(&"static") // TODO: