use crate::auth::Authorized; use crate::status_json::StatusJson; use chrono::{Duration, Local, NaiveDate}; use itertools::Itertools; use rocket::{get, response::content::Html, State}; use rocket_dyn_templates::Template; use serde::{Deserialize, Serialize}; use std::borrow::Cow; use std::iter::{once, repeat}; pub struct BirthDate(pub NaiveDate); #[get("/weeks")] pub fn weeks( _auth: Authorized, birth_date: &State, ) -> Result, StatusJson> { type Color<'a> = Cow<'a, str>; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] struct Style<'a> { border: Color<'a>, fill: Option>, } #[derive(Debug, Clone, Serialize, Deserialize)] struct PeriodCtx<'a> { style: Style<'a>, weeks: Vec<()>, } impl<'a> Style<'a> { fn border(border: C) -> Self where C: Into>, { Style { border: border.into(), fill: None, } } fn fill(border: C, fill: C) -> Self where C: Into>, { Style { border: border.into(), fill: Some(fill.into()), } } } #[derive(Debug, Serialize, Deserialize)] struct Ctx<'a> { weeks_left: i64, periods: Vec>, } let now = Local::now().date(); let birth_date = birth_date.0; let lived: Duration = now.naive_local() - birth_date; let one_year = Duration::days(365); let life_expectancy = (one_year * 81).num_weeks(); let uncertainty = (one_year * 20).num_weeks(); let childhood_end = (one_year * 12).num_weeks(); let teenage_end = (one_year * 20).num_weeks(); let color_birth = "green"; let color_child = "#ff0"; let color_teen = "#f33"; let color_adult = "#f0f"; let color_today = "wheat"; let color_future = "#eee"; let context = Ctx { weeks_left: life_expectancy - lived.num_weeks(), periods: once(Style::fill(color_birth, color_birth)) // childhood .chain((1..childhood_end).map(|_| Style::border(color_child))) // teens .chain((childhood_end..teenage_end).map(|_| Style::border(color_teen))) // adulthood .chain((teenage_end..).map(|_| Style::border(color_adult))) // take from above for lived number of weeks .take(lived.num_weeks() as usize - 1) // add a week for this week .chain(once(Style::fill(color_today, color_today))) // fill remaining weeks until death .chain(repeat(Style::border(color_future))) .take((life_expectancy - uncertainty * 2) as usize) // add some fading around expected life span .chain((0..uncertainty).rev().map(|i| { let m = u8::MAX as i64; let alpha = ((m * i) / (uncertainty)) as u8; Style::border(format!("#eeeeee{:02x}", alpha)) })) // group weeks by color to save space .group_by(|style| style.clone()) .into_iter() .map(|(style, weeks)| PeriodCtx { style, weeks: vec![(); weeks.count()], }) .collect(), }; Ok(Html(Template::render("weeks", &context))) }