diff --git a/src/eval.rs b/src/eval.rs index bf6a826..cb1cb8b 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -1,14 +1,17 @@ -use std::collections::HashSet; use std::result; use chrono::{Datelike, NaiveDate}; -use crate::files::commands::{Birthday, Command, File, Note, Spec, Task}; +use crate::eval::entry::EntryDate; +use crate::files::commands::{Birthday, Command}; +use crate::files::{Files, Source}; -use self::entries::{DateRange, Entry, EntryKind, EntryMap}; +use self::entry::{Entry, EntryKind, EntryMap}; +pub use self::range::DateRange; mod delta; -pub mod entries; +mod entry; +mod range; #[derive(Debug, thiserror::Error)] pub enum Error { @@ -18,114 +21,85 @@ pub enum Error { type Result = result::Result; -fn eval_spec(spec: &Spec, index: usize, map: &mut EntryMap) -> Result<()> { - Ok(()) +struct Eval { + map: EntryMap, + source: Source, } -fn eval_task(task: &Task, index: usize, range: DateRange) -> Result> { - let mut map = EntryMap::new(range); - - map.from = task.from; - map.until = task.until; - - for date in &task.except { - map.block(*date); +impl Eval { + pub fn new(range: DateRange, source: Source) -> Self { + Self { + map: EntryMap::new(range), + source, + } } - for spec in &task.when { - eval_spec(spec, index, &mut map)?; - } + fn eval_birthday(&mut self, birthday: &Birthday) { + for year in self.map.range().years() { + let when = &birthday.when; + let mut title = birthday.title.clone(); - let done: HashSet = task - .done - .iter() - .filter_map(|done| done.refering_to) - .collect(); + if when.year_known { + let age = year - when.date.year(); + if age < 0 { + continue; + } + title.push_str(&format!(" ({})", age)); + } - for (date, entry) in map.map.iter_mut() { - if let Some(entry) = entry { - if done.contains(date) { - entry.kind.done(); + if let Some(date) = when.date.with_year(year) { + let entry = Entry { + kind: EntryKind::Birthday, + title: title.clone(), + desc: birthday.desc.clone(), + source: self.source, + date: EntryDate::Date { root: date }, + }; + self.map.insert(date, entry); + } else { + assert_eq!(when.date.month(), 2); + assert_eq!(when.date.day(), 29); + let date = NaiveDate::from_ymd(year, 2, 28); + let entry = Entry { + kind: EntryKind::Birthday, + title: format!("{} (first half)", title), + desc: birthday.desc.clone(), + source: self.source, + date: EntryDate::Date { root: date }, + }; + self.map.insert(date, entry); + + let date = NaiveDate::from_ymd(year, 3, 1); + let entry = Entry { + kind: EntryKind::Birthday, + title: format!("{} (second half)", title), + desc: birthday.desc.clone(), + source: self.source, + date: EntryDate::Date { root: date }, + }; + self.map.insert(date, entry); } } } - Ok(map.drain()) -} - -fn eval_note(task: &Note, index: usize, range: DateRange) -> Result> { - let mut map = EntryMap::new(range); - - map.from = task.from; - map.until = task.until; - - for date in &task.except { - map.block(*date); - } - - for spec in &task.when { - eval_spec(spec, index, &mut map)?; - } - - Ok(map.drain()) -} - -fn eval_birthday(bd: &Birthday, index: usize, range: DateRange) -> Result> { - let mut map = EntryMap::new(range); - - for year in range.years() { - if bd.when.year_known && year < bd.when.date.year() { - continue; + pub fn eval(&mut self, command: &Command) -> Result> { + // This function fills the entry map and then drains it again, so in + // theory, the struct can even be reused afterwards. + match command { + Command::Task(task) => todo!(), + Command::Note(note) => todo!(), + Command::Birthday(birthday) => self.eval_birthday(birthday), } + Ok(self.map.drain()) + } +} - let title = if bd.when.year_known { - let age = year - bd.when.date.year(); - format!("{} ({})", bd.title, age) - } else { - bd.title.to_string() - }; - - match bd.when.date.with_year(year) { - Some(date) => { - let mut entry = Entry::new(index, EntryKind::Birthday, title); - entry.start = Some(date); - map.insert(date, entry); - } - None => { - // We must've hit a non-leapyear - assert_eq!(bd.when.date.month(), 2); - assert_eq!(bd.when.date.day(), 29); - - let first_date = NaiveDate::from_ymd(year, 2, 28); - let first_title = format!("{} (first half)", title); - let mut first_entry = Entry::new(index, EntryKind::Birthday, first_title); - first_entry.start = Some(first_date); - map.insert(first_date, first_entry); - - let second_date = NaiveDate::from_ymd(year, 3, 1); - let second_title = format!("{} (second half)", title); - let mut second_entry = Entry::new(index, EntryKind::Birthday, second_title); - second_entry.start = Some(second_date); - map.insert(second_date, second_entry); - } +impl Files { + pub fn eval(&self, range: DateRange) -> Result> { + let mut result = vec![]; + for command in self.commands() { + result.append(&mut Eval::new(range, command.source).eval(command.command)?); } - } - - Ok(map.drain()) -} - -fn eval_command(command: &Command, index: usize, range: DateRange) -> Result> { - match command { - Command::Task(task) => eval_task(task, index, range), - Command::Note(note) => eval_note(note, index, range), - Command::Birthday(birthday) => eval_birthday(birthday, index, range), + Ok(result) } } - -pub fn eval(file: &File, range: DateRange) -> Result> { - let mut result = vec![]; - for (index, command) in file.commands.iter().enumerate() { - result.append(&mut eval_command(command, index, range)?); - } - Ok(result) -} diff --git a/src/eval/entries.rs b/src/eval/entries.rs deleted file mode 100644 index 4b3b434..0000000 --- a/src/eval/entries.rs +++ /dev/null @@ -1,99 +0,0 @@ -use std::collections::HashMap; -use std::ops::RangeInclusive; - -use chrono::{Datelike, NaiveDate}; - -use crate::files::commands::Time; - -#[derive(Debug, PartialEq, Eq)] -pub enum EntryKind { - Task, - DoneTask, - Note, - Birthday, -} - -impl EntryKind { - pub fn done(&mut self) { - if matches!(self, Self::Task) { - *self = Self::DoneTask; - } - } -} - -#[derive(Debug)] -pub struct Entry { - pub kind: EntryKind, - pub title: String, - pub desc: Vec, - - /// Index in the source file - pub source: usize, - - pub start: Option, - pub start_time: Option