From 21739c73143230dfd76abf20e95fb6e2e7aa1caf Mon Sep 17 00:00:00 2001 From: Joscha Date: Thu, 25 Nov 2021 01:24:16 +0100 Subject: [PATCH] Implement basic structure for task evaluation --- src/eval.rs | 150 ++++++++++++++++++++++++++++++++++-------- src/eval/entry.rs | 63 ++++-------------- src/files/commands.rs | 15 ++++- 3 files changed, 146 insertions(+), 82 deletions(-) diff --git a/src/eval.rs b/src/eval.rs index 59df37d..5731879 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -1,12 +1,13 @@ +use std::cmp; use std::result; use chrono::{Datelike, NaiveDate}; -use crate::eval::entry::EntryDate; -use crate::files::commands::{Birthday, Command, Note, Spec, Task}; +use crate::files::commands::{Birthday, Command, DoneDate, Note, Spec, Task}; use crate::files::{Files, Source, SourcedCommand}; -use self::entry::{Entry, EntryKind, EntryMap}; +use self::entry::EntryMap; +pub use self::entry::{Entry, EntryKind}; pub use self::range::DateRange; mod delta; @@ -30,43 +31,136 @@ impl Eval { fn eval_spec( &mut self, spec: &Spec, - new_entry: impl Fn(Source, EntryDate) -> Entry, + last_done: Option, + new_entry: impl Fn(Source, Option) -> Entry, ) -> Result<()> { todo!() } + fn eval_dones(&mut self, task: &Task) { + for done in &task.done { + let entry = Entry { + kind: EntryKind::TaskDone(done.done_at), + title: task.title.clone(), + desc: task.desc.clone(), + source: self.source, + date: done.date, + }; + self.map.insert(entry); + } + } + + fn determine_last_done(task: &Task) -> Option { + task.done + .iter() + .flat_map(|done| done.date.iter()) + .map(|d| d.root()) + .max() + } + + fn determine_task_range(&self, task: &Task, last_done: Option) -> Option { + let mut from = self.map.range.from(); + let mut until = self.map.range.until(); + + if let Some(last_done) = last_done { + from = cmp::min(from, last_done); + } + + if let Some(task_from) = task.from { + from = cmp::max(from, task_from); + } + if let Some(task_until) = task.until { + until = cmp::min(until, task_until); + } + + if from <= until { + Some(DateRange::new(from, until)) + } else { + None + } + } + + fn eval_task(&mut self, task: &Task) -> Result<()> { + self.eval_dones(task); + + if task.done.iter().any(|done| done.date.is_none()) { + return Ok(()); + } + + if task.when.is_empty() { + self.map.insert(Entry { + kind: EntryKind::Task, + title: task.title.clone(), + desc: task.desc.clone(), + source: self.source, + date: None, + }); + } else { + let last_done = Self::determine_last_done(task); + + if let Some(range) = self.determine_task_range(task, last_done) { + self.map.range = range; + } else { + return Ok(()); + } + + for except in &task.except { + self.map.block(*except); + } + + for spec in &task.when { + self.eval_spec(spec, last_done, |source, date| Entry { + kind: EntryKind::Note, + title: task.title.clone(), + desc: task.desc.clone(), + source, + date, + })?; + } + } + Ok(()) + } + + fn determine_note_range(&self, note: &Note) -> Option { + let mut from = self.map.range.from(); + let mut until = self.map.range.until(); + + if let Some(task_from) = note.from { + from = cmp::max(from, task_from); + } + if let Some(task_until) = note.until { + until = cmp::max(until, task_until); + } + + if from <= until { + Some(DateRange::new(from, until)) + } else { + None + } + } + fn eval_note(&mut self, note: &Note) -> Result<()> { if note.when.is_empty() { - let entry = Entry { + self.map.insert(Entry { kind: EntryKind::Note, title: note.title.clone(), desc: note.desc.clone(), source: self.source, - date: EntryDate::None, - }; - self.map.insert(entry); + date: None, + }); } else { - if let Some(from) = note.from { - if self.map.range().until() < from { - return Ok(()); - } - if self.map.range().from() < from { - self.map.set_from(from); - } - } - if let Some(until) = note.until { - if until < self.map.range().from() { - return Ok(()); - } - if until < self.map.range().until() { - self.map.set_until(until); - } + if let Some(range) = self.determine_note_range(note) { + self.map.range = range; + } else { + return Ok(()); } + for except in ¬e.except { self.map.block(*except); } + for spec in ¬e.when { - self.eval_spec(spec, |source, date| Entry { + self.eval_spec(spec, None, |source, date| Entry { kind: EntryKind::Note, title: note.title.clone(), desc: note.desc.clone(), @@ -79,7 +173,7 @@ impl Eval { } fn eval_birthday(&mut self, birthday: &Birthday) { - for year in self.map.range().years() { + for year in self.map.range.years() { let when = &birthday.when; let mut title = birthday.title.clone(); @@ -97,7 +191,7 @@ impl Eval { title: title.clone(), desc: birthday.desc.clone(), source: self.source, - date: EntryDate::Date { root: date }, + date: Some(DoneDate::Date { root: date }), }; self.map.insert(entry); } else { @@ -110,7 +204,7 @@ impl Eval { title: format!("{} (first half)", title), desc: birthday.desc.clone(), source: self.source, - date: EntryDate::Date { root: date }, + date: Some(DoneDate::Date { root: date }), }; self.map.insert(entry); @@ -120,7 +214,7 @@ impl Eval { title: format!("{} (second half)", title), desc: birthday.desc.clone(), source: self.source, - date: EntryDate::Date { root: date }, + date: Some(DoneDate::Date { root: date }), }; self.map.insert(entry); } diff --git a/src/eval/entry.rs b/src/eval/entry.rs index d02df55..292a1df 100644 --- a/src/eval/entry.rs +++ b/src/eval/entry.rs @@ -2,53 +2,19 @@ use std::collections::HashMap; use chrono::NaiveDate; -use crate::files::commands::Time; +use crate::files::commands::{DoneDate, Time}; use crate::files::Source; use super::range::DateRange; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum EntryKind { Task, - DoneTask, + TaskDone(NaiveDate), Note, Birthday, } -#[derive(Debug)] -pub enum EntryDate { - None, - Date { - root: NaiveDate, - }, - DateWithTime { - root: NaiveDate, - root_time: Time, - }, - DateToDate { - root: NaiveDate, - other: NaiveDate, - }, - DateToDateWithTime { - root: NaiveDate, - root_time: Time, - other: NaiveDate, - other_time: Time, - }, -} - -impl EntryDate { - pub fn root(&self) -> Option { - match self { - EntryDate::None => None, - EntryDate::Date { root, .. } => Some(*root), - EntryDate::DateWithTime { root, .. } => Some(*root), - EntryDate::DateToDate { root, .. } => Some(*root), - EntryDate::DateToDateWithTime { root, .. } => Some(*root), - } - } -} - #[derive(Debug)] pub struct Entry { pub kind: EntryKind, @@ -56,11 +22,11 @@ pub struct Entry { pub desc: Vec, pub source: Source, - pub date: EntryDate, + pub date: Option, } pub struct EntryMap { - range: DateRange, + pub range: DateRange, map: HashMap>, undated: Vec, } @@ -74,18 +40,6 @@ impl EntryMap { } } - pub fn range(&self) -> DateRange { - self.range - } - - pub fn set_from(&mut self, from: NaiveDate) { - self.range = DateRange::new(from, self.range.until()); - } - - pub fn set_until(&mut self, until: NaiveDate) { - self.range = DateRange::new(self.range.from(), until); - } - pub fn block(&mut self, date: NaiveDate) { if self.range.contains(date) { self.map.entry(date).or_insert(None); @@ -93,9 +47,14 @@ impl EntryMap { } pub fn insert(&mut self, entry: Entry) { - if let Some(date) = entry.date.root() { + if let Some(date) = entry.date { + let date = date.root(); if self.range.contains(date) { self.map.entry(date).or_insert(Some(entry)); + } else if let EntryKind::TaskDone(done_date) = entry.kind { + if self.range.contains(done_date) { + self.map.entry(date).or_insert(Some(entry)); + } } } else { self.undated.push(entry); diff --git a/src/files/commands.rs b/src/files/commands.rs index 0e778f6..f47cf60 100644 --- a/src/files/commands.rs +++ b/src/files/commands.rs @@ -1,6 +1,6 @@ use chrono::NaiveDate; -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub struct Time { pub hour: u8, pub min: u8, @@ -285,7 +285,7 @@ pub enum Spec { Formula(FormulaSpec), } -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub enum DoneDate { Date { root: NaiveDate, @@ -306,6 +306,17 @@ pub enum DoneDate { }, } +impl DoneDate { + pub fn root(&self) -> NaiveDate { + match self { + DoneDate::Date { root } => *root, + DoneDate::DateWithTime { root, .. } => *root, + DoneDate::DateToDate { root, .. } => *root, + DoneDate::DateToDateWithTime { root, .. } => *root, + } + } +} + #[derive(Debug)] pub struct Done { pub date: Option,