Restructure eval module
This commit is contained in:
parent
817732abf6
commit
35184e21e9
5 changed files with 208 additions and 199 deletions
158
src/eval.rs
158
src/eval.rs
|
|
@ -1,14 +1,17 @@
|
||||||
use std::collections::HashSet;
|
|
||||||
use std::result;
|
use std::result;
|
||||||
|
|
||||||
use chrono::{Datelike, NaiveDate};
|
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;
|
mod delta;
|
||||||
pub mod entries;
|
mod entry;
|
||||||
|
mod range;
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
|
@ -18,114 +21,85 @@ pub enum Error {
|
||||||
|
|
||||||
type Result<T> = result::Result<T, Error>;
|
type Result<T> = result::Result<T, Error>;
|
||||||
|
|
||||||
fn eval_spec(spec: &Spec, index: usize, map: &mut EntryMap) -> Result<()> {
|
struct Eval {
|
||||||
Ok(())
|
map: EntryMap,
|
||||||
|
source: Source,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_task(task: &Task, index: usize, range: DateRange) -> Result<Vec<Entry>> {
|
impl Eval {
|
||||||
let mut map = EntryMap::new(range);
|
pub fn new(range: DateRange, source: Source) -> Self {
|
||||||
|
Self {
|
||||||
map.from = task.from;
|
map: EntryMap::new(range),
|
||||||
map.until = task.until;
|
source,
|
||||||
|
|
||||||
for date in &task.except {
|
|
||||||
map.block(*date);
|
|
||||||
}
|
|
||||||
|
|
||||||
for spec in &task.when {
|
|
||||||
eval_spec(spec, index, &mut map)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let done: HashSet<NaiveDate> = task
|
|
||||||
.done
|
|
||||||
.iter()
|
|
||||||
.filter_map(|done| done.refering_to)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
for (date, entry) in map.map.iter_mut() {
|
|
||||||
if let Some(entry) = entry {
|
|
||||||
if done.contains(date) {
|
|
||||||
entry.kind.done();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(map.drain())
|
fn eval_birthday(&mut self, birthday: &Birthday) {
|
||||||
}
|
for year in self.map.range().years() {
|
||||||
|
let when = &birthday.when;
|
||||||
|
let mut title = birthday.title.clone();
|
||||||
|
|
||||||
fn eval_note(task: &Note, index: usize, range: DateRange) -> Result<Vec<Entry>> {
|
if when.year_known {
|
||||||
let mut map = EntryMap::new(range);
|
let age = year - when.date.year();
|
||||||
|
if age < 0 {
|
||||||
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<Vec<Entry>> {
|
|
||||||
let mut map = EntryMap::new(range);
|
|
||||||
|
|
||||||
for year in range.years() {
|
|
||||||
if bd.when.year_known && year < bd.when.date.year() {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
title.push_str(&format!(" ({})", age));
|
||||||
|
}
|
||||||
|
|
||||||
let title = if bd.when.year_known {
|
if let Some(date) = when.date.with_year(year) {
|
||||||
let age = year - bd.when.date.year();
|
let entry = Entry {
|
||||||
format!("{} ({})", bd.title, age)
|
kind: EntryKind::Birthday,
|
||||||
} else {
|
title: title.clone(),
|
||||||
bd.title.to_string()
|
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);
|
||||||
|
|
||||||
match bd.when.date.with_year(year) {
|
let date = NaiveDate::from_ymd(year, 3, 1);
|
||||||
Some(date) => {
|
let entry = Entry {
|
||||||
let mut entry = Entry::new(index, EntryKind::Birthday, title);
|
kind: EntryKind::Birthday,
|
||||||
entry.start = Some(date);
|
title: format!("{} (second half)", title),
|
||||||
map.insert(date, entry);
|
desc: birthday.desc.clone(),
|
||||||
}
|
source: self.source,
|
||||||
None => {
|
date: EntryDate::Date { root: date },
|
||||||
// We must've hit a non-leapyear
|
};
|
||||||
assert_eq!(bd.when.date.month(), 2);
|
self.map.insert(date, entry);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(map.drain())
|
pub fn eval(&mut self, command: &Command) -> Result<Vec<Entry>> {
|
||||||
}
|
// This function fills the entry map and then drains it again, so in
|
||||||
|
// theory, the struct can even be reused afterwards.
|
||||||
fn eval_command(command: &Command, index: usize, range: DateRange) -> Result<Vec<Entry>> {
|
|
||||||
match command {
|
match command {
|
||||||
Command::Task(task) => eval_task(task, index, range),
|
Command::Task(task) => todo!(),
|
||||||
Command::Note(note) => eval_note(note, index, range),
|
Command::Note(note) => todo!(),
|
||||||
Command::Birthday(birthday) => eval_birthday(birthday, index, range),
|
Command::Birthday(birthday) => self.eval_birthday(birthday),
|
||||||
|
}
|
||||||
|
Ok(self.map.drain())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval(file: &File, range: DateRange) -> Result<Vec<Entry>> {
|
impl Files {
|
||||||
|
pub fn eval(&self, range: DateRange) -> Result<Vec<Entry>> {
|
||||||
let mut result = vec![];
|
let mut result = vec![];
|
||||||
for (index, command) in file.commands.iter().enumerate() {
|
for command in self.commands() {
|
||||||
result.append(&mut eval_command(command, index, range)?);
|
result.append(&mut Eval::new(range, command.source).eval(command.command)?);
|
||||||
}
|
}
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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<String>,
|
|
||||||
|
|
||||||
/// Index in the source file
|
|
||||||
pub source: usize,
|
|
||||||
|
|
||||||
pub start: Option<NaiveDate>,
|
|
||||||
pub start_time: Option<Time>,
|
|
||||||
pub end: Option<NaiveDate>,
|
|
||||||
pub end_time: Option<Time>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Entry {
|
|
||||||
pub fn new(source: usize, kind: EntryKind, title: String) -> Self {
|
|
||||||
Self {
|
|
||||||
kind,
|
|
||||||
title,
|
|
||||||
desc: vec![],
|
|
||||||
source,
|
|
||||||
start: None,
|
|
||||||
start_time: None,
|
|
||||||
end: None,
|
|
||||||
end_time: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct DateRange {
|
|
||||||
pub from: NaiveDate,
|
|
||||||
pub until: NaiveDate,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DateRange {
|
|
||||||
pub fn new(from: NaiveDate, until: NaiveDate) -> Self {
|
|
||||||
assert!(from <= until);
|
|
||||||
Self { from, until }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn years(&self) -> RangeInclusive<i32> {
|
|
||||||
self.from.year()..=self.until.year()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct EntryMap {
|
|
||||||
pub range: DateRange,
|
|
||||||
pub from: Option<NaiveDate>,
|
|
||||||
pub until: Option<NaiveDate>,
|
|
||||||
pub map: HashMap<NaiveDate, Option<Entry>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EntryMap {
|
|
||||||
pub fn new(range: DateRange) -> Self {
|
|
||||||
Self {
|
|
||||||
range,
|
|
||||||
from: None,
|
|
||||||
until: None,
|
|
||||||
map: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn block(&mut self, date: NaiveDate) {
|
|
||||||
self.map.entry(date).or_insert(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert(&mut self, date: NaiveDate, entry: Entry) {
|
|
||||||
self.map.entry(date).or_insert(Some(entry));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn drain(&mut self) -> Vec<Entry> {
|
|
||||||
self.map.drain().filter_map(|(_, entry)| entry).collect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
94
src/eval/entry.rs
Normal file
94
src/eval/entry.rs
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use chrono::NaiveDate;
|
||||||
|
|
||||||
|
use crate::files::commands::Time;
|
||||||
|
use crate::files::Source;
|
||||||
|
|
||||||
|
use super::range::DateRange;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum EntryKind {
|
||||||
|
Task,
|
||||||
|
DoneTask,
|
||||||
|
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<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)]
|
||||||
|
pub struct Entry {
|
||||||
|
pub kind: EntryKind,
|
||||||
|
pub title: String,
|
||||||
|
pub desc: Vec<String>,
|
||||||
|
|
||||||
|
pub source: Source,
|
||||||
|
pub date: EntryDate,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EntryMap {
|
||||||
|
range: DateRange,
|
||||||
|
map: HashMap<NaiveDate, Option<Entry>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EntryMap {
|
||||||
|
pub fn new(range: DateRange) -> Self {
|
||||||
|
Self {
|
||||||
|
range,
|
||||||
|
map: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn range(&self) -> DateRange {
|
||||||
|
self.range
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn block(&mut self, date: NaiveDate) {
|
||||||
|
if self.range.contains(date) {
|
||||||
|
self.map.entry(date).or_insert(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert(&mut self, date: NaiveDate, entry: Entry) {
|
||||||
|
if self.range.contains(date) {
|
||||||
|
self.map.entry(date).or_insert(Some(entry));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn drain(&mut self) -> Vec<Entry> {
|
||||||
|
self.map.drain().filter_map(|(_, entry)| entry).collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
32
src/eval/range.rs
Normal file
32
src/eval/range.rs
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
use std::ops::RangeInclusive;
|
||||||
|
|
||||||
|
use chrono::{Datelike, NaiveDate};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct DateRange {
|
||||||
|
from: NaiveDate,
|
||||||
|
until: NaiveDate,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DateRange {
|
||||||
|
pub fn new(from: NaiveDate, until: NaiveDate) -> Self {
|
||||||
|
assert!(from <= until);
|
||||||
|
Self { from, until }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn contains(&self, date: NaiveDate) -> bool {
|
||||||
|
self.from <= date && date <= self.until
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from(&self) -> NaiveDate {
|
||||||
|
self.from
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn until(&self) -> NaiveDate {
|
||||||
|
self.until
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn years(&self) -> RangeInclusive<i32> {
|
||||||
|
self.from.year()..=self.until.year()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,8 +5,10 @@
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use chrono::NaiveDate;
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
|
use crate::eval::DateRange;
|
||||||
use crate::files::Files;
|
use crate::files::Files;
|
||||||
|
|
||||||
mod eval;
|
mod eval;
|
||||||
|
|
@ -24,6 +26,12 @@ fn main() -> anyhow::Result<()> {
|
||||||
let mut files = Files::load(&opt.file)?;
|
let mut files = Files::load(&opt.file)?;
|
||||||
println!("{}", files.now().format("%F %T %Z"));
|
println!("{}", files.now().format("%F %T %Z"));
|
||||||
|
|
||||||
|
let range = DateRange::new(
|
||||||
|
NaiveDate::from_ymd(2021, 11, 20),
|
||||||
|
NaiveDate::from_ymd(2021, 11, 26),
|
||||||
|
);
|
||||||
|
println!("{:#?}", files.eval(range));
|
||||||
|
|
||||||
files.mark_all_dirty();
|
files.mark_all_dirty();
|
||||||
files.save()?;
|
files.save()?;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue