From ee2e5f4e975916c14c89338f203b072a990aeb7f Mon Sep 17 00:00:00 2001 From: Joscha Date: Sat, 11 Dec 2021 22:39:34 +0100 Subject: [PATCH] Revamp layouting --- src/cli.rs | 5 +- src/cli/layout.rs | 281 ++++++++++++++++++++++++++++++---------------- 2 files changed, 187 insertions(+), 99 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index bab865f..aff5edf 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -20,8 +20,7 @@ pub fn run() -> anyhow::Result<()> { let opt = Opt::from_args(); let files = Files::load(&opt.file)?; - let now = files.now(); - let today = now.date().naive_local(); + let now = files.now().naive_local(); let range = DateRange::new( NaiveDate::from_ymd(2021, 1, 1), @@ -32,7 +31,7 @@ pub fn run() -> anyhow::Result<()> { let entries = files.eval(EntryMode::Relevant, range)?; println!("{:#?}", entries); - let mut layout = Layout::new(range, today); + let mut layout = Layout::new(range, now); layout.layout(&files, &entries); println!("{:#?}", layout); diff --git a/src/cli/layout.rs b/src/cli/layout.rs index 6ebb0bb..177ba2c 100644 --- a/src/cli/layout.rs +++ b/src/cli/layout.rs @@ -1,147 +1,236 @@ use std::collections::HashMap; -use chrono::NaiveDate; +use chrono::{NaiveDate, NaiveDateTime}; use crate::eval::{DateRange, Dates, Entry, EntryKind}; +use crate::files::commands::Command; use crate::files::primitives::Time; use crate::files::Files; #[derive(Debug)] -pub struct TimedLayout { - pub ending: Vec, - pub at: Vec, - pub starting: Vec, -} - -impl TimedLayout { - pub fn new() -> Self { - Self { - ending: vec![], - at: vec![], - starting: vec![], - } - } -} - -#[derive(Debug)] -pub struct DayLayout { - pub ending: Vec, - pub timed: HashMap, - pub at: Vec, - pub other: Vec, - pub starting: Vec, -} - -impl DayLayout { - pub fn new() -> Self { - Self { - ending: vec![], - timed: HashMap::new(), - at: vec![], - other: vec![], - starting: vec![], - } - } +pub enum LayoutEntry { + End(usize), + Now(Time), + TimedEnd(usize, Time), + TimedAt(usize, Time), + TimedStart(usize, Time), + ReminderSince(usize, i64), + At(usize), + ReminderWhile(usize, i64), + Undated(usize), + Start(usize), + ReminderUntil(usize, i64), } #[derive(Debug)] pub struct Layout { pub range: DateRange, pub today: NaiveDate, - pub days: HashMap, + pub time: Time, + pub earlier: Vec, + pub days: HashMap>, } impl Layout { - pub fn new(range: DateRange, today: NaiveDate) -> Self { - let mut days = HashMap::new(); - for day in range.days() { - days.insert(day, DayLayout::new()); + pub fn new(range: DateRange, now: NaiveDateTime) -> Self { + Self { + range, + today: now.date(), + time: now.time().into(), + earlier: vec![], + days: range.days().map(|d| (d, vec![])).collect(), } - Self { range, today, days } } pub fn layout(&mut self, files: &Files, entries: &[Entry]) { + self.insert(self.today, LayoutEntry::Now(self.time)); + let mut commands = entries .iter() .enumerate() .map(|(i, e)| (i, e, files.command(e.source))) .collect::>(); - // Sort entries (maintaining the keys) so the output is more deterministic - commands.sort_by_key(|(_, _, c)| c.title()); - commands.sort_by_key(|(_, e, _)| e.dates.map(|d| (d.end(), d.end_time()))); - commands.sort_by_key(|(_, e, _)| e.dates.map(|d| (d.start(), d.start_time()))); + Self::sort_entries(&mut commands); for (index, entry, _) in commands { - self.insert(index, entry); + self.layout_entry(index, entry); + } + + for (_, day) in self.days.iter_mut() { + Self::sort_day(day); } } - fn insert(&mut self, index: usize, entry: &Entry) { + fn layout_entry(&mut self, index: usize, entry: &Entry) { + match entry.kind { + EntryKind::Task => self.layout_task(index, entry), + EntryKind::TaskDone(at) => self.layout_task_done(index, entry, at), + EntryKind::Note | EntryKind::Birthday(_) => self.layout_note(index, entry), + } + } + + fn layout_task(&mut self, index: usize, entry: &Entry) { if let Some(dates) = entry.dates { - self.insert_dated(index, dates); - if let EntryKind::TaskDone(at) = entry.kind { - self.insert_other(at, index); + let (start, end) = dates.start_end(); + if (start - self.today).num_days() < 7 { + // TODO Make this adjustable, maybe even per-command + let days = (start - self.today).num_days(); + self.insert(self.today, LayoutEntry::ReminderUntil(index, days)); + } else if start < self.today && self.today < end { + let days = (end - self.today).num_days(); + self.insert(self.today, LayoutEntry::ReminderWhile(index, days)); + } else if end < self.today { + let days = (self.today - end).num_days(); + self.insert(self.today, LayoutEntry::ReminderSince(index, days)); + } + self.layout_dated_entry(index, dates); + } else { + self.insert(self.today, LayoutEntry::Undated(index)); + } + } + + fn layout_task_done(&mut self, index: usize, entry: &Entry, at: NaiveDate) { + if let Some(dates) = entry.dates { + if at > dates.end() { + let days = (at - dates.end()).num_days(); + self.insert(at, LayoutEntry::ReminderSince(index, days)); + } + self.layout_dated_entry(index, dates); + } else { + // Treat the task as if its date was its completion time + self.layout_dated_entry(index, Dates::new(at, at)); + } + } + + fn layout_note(&mut self, index: usize, entry: &Entry) { + if let Some(dates) = entry.dates { + let (start, end) = dates.start_end(); + if start < self.range.from() && self.range.until() < end { + // This note applies to the current day, but it won't appear if + // we just layout it as a dated entry, so instead we add it as a + // reminder. Since we are usually more interested in when + // something ends than when it starts, we count the days until + // the end. + let days = (end - self.today).num_days(); + self.insert(self.today, LayoutEntry::ReminderWhile(index, days)); + } else { + self.layout_dated_entry(index, dates); } } else { - self.insert_other(self.today, index); + self.insert(self.today, LayoutEntry::Undated(index)); } } - fn insert_dated(&mut self, index: usize, dates: Dates) { + fn layout_dated_entry(&mut self, index: usize, dates: Dates) { let (start, end) = dates.start_end(); - if start < self.range.from() && self.range.until() < end { - self.insert_other(self.today, index); - } else if let Some((date, time)) = dates.point_in_time() { - self.insert_at(date, time, index); - } else { - let (start_time, end_time) = match dates.start_end_time() { - Some((s, e)) => (Some(s), Some(e)), - None => (None, None), + if let Some((date, time)) = dates.point_in_time() { + let entry = match time { + Some(time) => LayoutEntry::TimedAt(index, time), + None => LayoutEntry::At(index), }; - self.insert_start(start, start_time, index); - self.insert_end(end, end_time, index); - } - } - - fn insert_f(&mut self, date: NaiveDate, f: impl FnOnce(&mut DayLayout)) { - if let Some(l) = self.days.get_mut(&date) { - f(l); - } - } - - fn insert_timed_f(&mut self, date: NaiveDate, time: Time, f: impl FnOnce(&mut TimedLayout)) { - if let Some(l) = self.days.get_mut(&date) { - let tl = l.timed.entry(time).or_insert_with(TimedLayout::new); - f(tl); - } - } - - fn insert_start(&mut self, date: NaiveDate, time: Option