102 lines
3.2 KiB
Rust
102 lines
3.2 KiB
Rust
use askama::Template;
|
|
use axum::{
|
|
response::{IntoResponse, Response, Html},
|
|
http::StatusCode,
|
|
Extension,
|
|
};
|
|
|
|
use diesel::prelude::*;
|
|
use chrono::{offset::Local, Datelike, Duration, NaiveDate, TimeZone};
|
|
|
|
use libticker::{
|
|
schema::{self, events::dsl::events},
|
|
model::Event, config::Config,
|
|
};
|
|
use crate::AppState;
|
|
|
|
|
|
fn fix_url(s: &str) -> String {
|
|
if s.starts_with("http:") || s.starts_with("https:") {
|
|
s.to_owned()
|
|
} else {
|
|
format!("http://{}", s)
|
|
}
|
|
}
|
|
|
|
struct DayEvents<'e> {
|
|
date: NaiveDate,
|
|
month: &'e str,
|
|
weekday: &'e str,
|
|
/// (event, url, color)
|
|
events: Vec<(&'e Event, Option<String>, &'e str)>,
|
|
}
|
|
|
|
/// assumes pre-sorted input
|
|
fn group_by_day<'e>(config: &'e Config, es: &'e [Event]) -> Vec<DayEvents<'e>> {
|
|
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 {
|
|
let date = prev_date.unwrap();
|
|
results.push(DayEvents {
|
|
date: date.clone(),
|
|
month: &config.months[date.month0() as usize],
|
|
weekday: &config.weekdays[date.weekday().num_days_from_monday() as usize],
|
|
events: es[date_start..i].iter()
|
|
.map(|event| {
|
|
let url = event.url.as_ref().map(|url| fix_url(url));
|
|
let color = config.calendars.get(&event.calendar)
|
|
.map(|calendar| &calendar.color[..])
|
|
.unwrap_or("grey");
|
|
(event, url, color)
|
|
}).collect(),
|
|
});
|
|
date_start = i;
|
|
}
|
|
}
|
|
prev_date = Some(event.dtstart.date());
|
|
}
|
|
|
|
results
|
|
}
|
|
|
|
#[derive(Template)]
|
|
#[template(path = "index.html")]
|
|
struct IndexTemplate<'a> {
|
|
days: Vec<DayEvents<'a>>,
|
|
}
|
|
|
|
pub async fn index(Extension(app_state): Extension<AppState>) -> Response {
|
|
let db = app_state.db.lock().unwrap();
|
|
let today = Local::now().date_naive().and_hms_opt(0, 0, 0).unwrap();
|
|
let limit = Local::now().date_naive().and_hms_opt(0, 0, 0).unwrap() +
|
|
Duration::days(app_state.config.upcoming_days.into());
|
|
let limit_past = Local::now().date_naive().and_hms_opt(0, 0, 0).unwrap() -
|
|
Duration::days(app_state.config.passed_days.into());
|
|
let es = events
|
|
.filter(schema::events::dtend.ge(&today))
|
|
.filter(schema::events::dtstart.ge(&limit_past))
|
|
.or_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::<Event>(&*db)
|
|
.unwrap();
|
|
let config = &app_state.config;
|
|
let days = group_by_day(config, &es);
|
|
let template = IndexTemplate {
|
|
days,
|
|
};
|
|
match template.render() {
|
|
Ok(rendered) =>
|
|
Html(rendered).into_response(),
|
|
Err(e) => (
|
|
StatusCode::INTERNAL_SERVER_ERROR,
|
|
format!("Failed to render template. Error: {}", e),
|
|
).into_response(),
|
|
}
|
|
}
|