ticker/ticker-serve/src/export.rs

65 lines
1.9 KiB
Rust

use askama::{Template, Text};
use axum::{
response::{IntoResponse, Response},
http::{StatusCode, HeaderMap, HeaderName, HeaderValue},
Extension,
};
use diesel::prelude::*;
use chrono::{offset::Local, Duration};
use libticker::{
schema::{self, events::dsl::events},
model::Event,
};
use crate::AppState;
#[derive(Template)]
#[template(path = "export.ics")]
struct IcsTemplate {
events: Vec<Event>,
}
pub async fn ics(Extension(app_state): Extension<AppState>) -> Response {
export(app_state, |es| (
IcsTemplate { events: es },
"text/calendar"
)).await
}
async fn export<F, T>(app_state: AppState, f: F) -> Response
where
F: FnOnce(Vec<Event>) -> (T, &'static str),
T: Template,
{
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 es: Vec<Event> = events
.filter(schema::events::dtend.ge(&today))
.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();
// .into_iter().collect::<Vec<Event>>();
let (template, content_type) = f(es);
match template.render() {
Ok(rendered) => {
let ics = rendered.replace('\n', "\r\n");
(
[(HeaderName::from_static("content-type"), HeaderValue::from_static(content_type))]
.iter().cloned()
.collect::<HeaderMap>(),
ics
).into_response()
}
Err(e) => (
StatusCode::INTERNAL_SERVER_ERROR,
format!("Failed to render template. Error: {}", e),
).into_response(),
}
}