Implement basic structure for task evaluation
This commit is contained in:
parent
11399468e7
commit
21739c7314
3 changed files with 146 additions and 82 deletions
150
src/eval.rs
150
src/eval.rs
|
|
@ -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 {
|
} else {
|
||||||
if let Some(from) = note.from {
|
if let Some(range) = self.determine_note_range(note) {
|
||||||
if self.map.range().until() < from {
|
self.map.range = range;
|
||||||
return Ok(());
|
} else {
|
||||||
}
|
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 ¬e.except {
|
for except in ¬e.except {
|
||||||
self.map.block(*except);
|
self.map.block(*except);
|
||||||
}
|
}
|
||||||
|
|
||||||
for spec in ¬e.when {
|
for spec in ¬e.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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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>,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue