Implement basic structure for task evaluation

This commit is contained in:
Joscha 2021-11-25 01:24:16 +01:00
parent 11399468e7
commit 21739c7314
3 changed files with 146 additions and 82 deletions

View file

@ -1,12 +1,13 @@
use std::cmp;
use std::result; use std::result;
use chrono::{Datelike, NaiveDate}; use chrono::{Datelike, NaiveDate};
use crate::eval::entry::EntryDate; use crate::files::commands::{Birthday, Command, DoneDate, Note, Spec, Task};
use crate::files::commands::{Birthday, Command, Note, Spec, Task};
use crate::files::{Files, Source, SourcedCommand}; 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; pub use self::range::DateRange;
mod delta; mod delta;
@ -30,43 +31,136 @@ impl Eval {
fn eval_spec( fn eval_spec(
&mut self, &mut self,
spec: &Spec, spec: &Spec,
new_entry: impl Fn(Source, EntryDate) -> Entry, last_done: Option<NaiveDate>,
new_entry: impl Fn(Source, Option<DoneDate>) -> Entry,
) -> Result<()> { ) -> Result<()> {
todo!() 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<NaiveDate> {
task.done
.iter()
.flat_map(|done| done.date.iter())
.map(|d| d.root())
.max()
}
fn determine_task_range(&self, task: &Task, last_done: Option<NaiveDate>) -> Option<DateRange> {
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<DateRange> {
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<()> { fn eval_note(&mut self, note: &Note) -> Result<()> {
if note.when.is_empty() { if note.when.is_empty() {
let entry = Entry { self.map.insert(Entry {
kind: EntryKind::Note, kind: EntryKind::Note,
title: note.title.clone(), title: note.title.clone(),
desc: note.desc.clone(), desc: note.desc.clone(),
source: self.source, source: self.source,
date: EntryDate::None, date: None,
}; });
self.map.insert(entry); } else {
if let Some(range) = self.determine_note_range(note) {
self.map.range = range;
} else { } else {
if let Some(from) = note.from {
if self.map.range().until() < from {
return Ok(()); 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);
}
}
for except in &note.except { for except in &note.except {
self.map.block(*except); self.map.block(*except);
} }
for spec in &note.when { for spec in &note.when {
self.eval_spec(spec, |source, date| Entry { self.eval_spec(spec, None, |source, date| Entry {
kind: EntryKind::Note, kind: EntryKind::Note,
title: note.title.clone(), title: note.title.clone(),
desc: note.desc.clone(), desc: note.desc.clone(),
@ -79,7 +173,7 @@ impl Eval {
} }
fn eval_birthday(&mut self, birthday: &Birthday) { 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 when = &birthday.when;
let mut title = birthday.title.clone(); let mut title = birthday.title.clone();
@ -97,7 +191,7 @@ impl Eval {
title: title.clone(), title: title.clone(),
desc: birthday.desc.clone(), desc: birthday.desc.clone(),
source: self.source, source: self.source,
date: EntryDate::Date { root: date }, date: Some(DoneDate::Date { root: date }),
}; };
self.map.insert(entry); self.map.insert(entry);
} else { } else {
@ -110,7 +204,7 @@ impl Eval {
title: format!("{} (first half)", title), title: format!("{} (first half)", title),
desc: birthday.desc.clone(), desc: birthday.desc.clone(),
source: self.source, source: self.source,
date: EntryDate::Date { root: date }, date: Some(DoneDate::Date { root: date }),
}; };
self.map.insert(entry); self.map.insert(entry);
@ -120,7 +214,7 @@ impl Eval {
title: format!("{} (second half)", title), title: format!("{} (second half)", title),
desc: birthday.desc.clone(), desc: birthday.desc.clone(),
source: self.source, source: self.source,
date: EntryDate::Date { root: date }, date: Some(DoneDate::Date { root: date }),
}; };
self.map.insert(entry); self.map.insert(entry);
} }

View file

@ -2,53 +2,19 @@ use std::collections::HashMap;
use chrono::NaiveDate; use chrono::NaiveDate;
use crate::files::commands::Time; use crate::files::commands::{DoneDate, Time};
use crate::files::Source; use crate::files::Source;
use super::range::DateRange; use super::range::DateRange;
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EntryKind { pub enum EntryKind {
Task, Task,
DoneTask, TaskDone(NaiveDate),
Note, Note,
Birthday, 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<NaiveDate> {
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)] #[derive(Debug)]
pub struct Entry { pub struct Entry {
pub kind: EntryKind, pub kind: EntryKind,
@ -56,11 +22,11 @@ pub struct Entry {
pub desc: Vec<String>, pub desc: Vec<String>,
pub source: Source, pub source: Source,
pub date: EntryDate, pub date: Option<DoneDate>,
} }
pub struct EntryMap { pub struct EntryMap {
range: DateRange, pub range: DateRange,
map: HashMap<NaiveDate, Option<Entry>>, map: HashMap<NaiveDate, Option<Entry>>,
undated: Vec<Entry>, undated: Vec<Entry>,
} }
@ -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) { pub fn block(&mut self, date: NaiveDate) {
if self.range.contains(date) { if self.range.contains(date) {
self.map.entry(date).or_insert(None); self.map.entry(date).or_insert(None);
@ -93,9 +47,14 @@ impl EntryMap {
} }
pub fn insert(&mut self, entry: Entry) { 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) { if self.range.contains(date) {
self.map.entry(date).or_insert(Some(entry)); 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 { } else {
self.undated.push(entry); self.undated.push(entry);

View file

@ -1,6 +1,6 @@
use chrono::NaiveDate; use chrono::NaiveDate;
#[derive(Debug)] #[derive(Debug, Clone, Copy)]
pub struct Time { pub struct Time {
pub hour: u8, pub hour: u8,
pub min: u8, pub min: u8,
@ -285,7 +285,7 @@ pub enum Spec {
Formula(FormulaSpec), Formula(FormulaSpec),
} }
#[derive(Debug)] #[derive(Debug, Clone, Copy)]
pub enum DoneDate { pub enum DoneDate {
Date { Date {
root: NaiveDate, 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)] #[derive(Debug)]
pub struct Done { pub struct Done {
pub date: Option<DoneDate>, pub date: Option<DoneDate>,