Make eval compile again
This commit is contained in:
parent
d8782fa28a
commit
de1f4a303f
4 changed files with 79 additions and 257 deletions
263
src/eval.rs
263
src/eval.rs
|
|
@ -1,262 +1,25 @@
|
||||||
use std::cmp;
|
use crate::files::Files;
|
||||||
use std::result;
|
|
||||||
|
|
||||||
use chrono::{Datelike, NaiveDate};
|
use self::command::CommandState;
|
||||||
|
use self::entry::Entries;
|
||||||
use crate::files::commands::{Birthday, Command, DoneDate, Note, Span, Spec, Task, Time};
|
pub use self::entry::{Entry, EntryKind, EntryMode};
|
||||||
use crate::files::{Files, Source, SourcedCommand};
|
pub use self::error::{Error, Result};
|
||||||
|
|
||||||
use self::entry::EntryMap;
|
|
||||||
pub use self::entry::{Entry, EntryKind};
|
|
||||||
pub use self::range::DateRange;
|
pub use self::range::DateRange;
|
||||||
|
|
||||||
mod date_spec;
|
mod command;
|
||||||
mod delta;
|
mod delta;
|
||||||
mod entry;
|
mod entry;
|
||||||
mod formula_spec;
|
mod error;
|
||||||
mod range;
|
mod range;
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Error {
|
|
||||||
DeltaInvalidStep {
|
|
||||||
span: Span,
|
|
||||||
start: NaiveDate,
|
|
||||||
start_time: Option<Time>,
|
|
||||||
prev: NaiveDate,
|
|
||||||
prev_time: Option<Time>,
|
|
||||||
},
|
|
||||||
DeltaNoTime {
|
|
||||||
span: Span,
|
|
||||||
start: NaiveDate,
|
|
||||||
prev: NaiveDate,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
type Result<T> = result::Result<T, Error>;
|
|
||||||
|
|
||||||
struct Eval {
|
|
||||||
map: EntryMap,
|
|
||||||
source: Source,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eval {
|
|
||||||
fn eval_spec(
|
|
||||||
&mut self,
|
|
||||||
spec: &Spec,
|
|
||||||
last_done: Option<NaiveDate>,
|
|
||||||
new_entry: impl Fn(Source, Option<DoneDate>) -> Entry,
|
|
||||||
) -> Result<()> {
|
|
||||||
match spec {
|
|
||||||
Spec::Date(spec) => self.eval_date_spec(spec.into(), last_done, new_entry),
|
|
||||||
Spec::Weekday(spec) => self.eval_formula_spec(spec.into(), new_entry),
|
|
||||||
Spec::Formula(spec) => self.eval_formula_spec(spec.into(), new_entry),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
root: 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,
|
|
||||||
root: 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,
|
|
||||||
root: 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<()> {
|
|
||||||
if note.when.is_empty() {
|
|
||||||
self.map.insert(Entry {
|
|
||||||
kind: EntryKind::Note,
|
|
||||||
title: note.title.clone(),
|
|
||||||
desc: note.desc.clone(),
|
|
||||||
source: self.source,
|
|
||||||
root: None,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
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, None, |source, date| Entry {
|
|
||||||
kind: EntryKind::Note,
|
|
||||||
title: note.title.clone(),
|
|
||||||
desc: note.desc.clone(),
|
|
||||||
source,
|
|
||||||
root: date,
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn eval_birthday(&mut self, birthday: &Birthday) {
|
|
||||||
for year in self.map.range.years() {
|
|
||||||
let when = &birthday.when;
|
|
||||||
let mut title = birthday.title.clone();
|
|
||||||
|
|
||||||
if when.year_known {
|
|
||||||
let age = year - when.date.year();
|
|
||||||
if age < 0 {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
title.push_str(&format!(" ({})", age));
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
root: Some(DoneDate::Date { root: date }),
|
|
||||||
};
|
|
||||||
self.map.insert(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,
|
|
||||||
root: Some(DoneDate::Date { root: date }),
|
|
||||||
};
|
|
||||||
self.map.insert(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,
|
|
||||||
root: Some(DoneDate::Date { root: date }),
|
|
||||||
};
|
|
||||||
self.map.insert(entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn eval(range: DateRange, command: &SourcedCommand<'_>) -> Result<Vec<Entry>> {
|
|
||||||
let mut map = Self {
|
|
||||||
map: EntryMap::new(range),
|
|
||||||
source: command.source,
|
|
||||||
};
|
|
||||||
match command.command {
|
|
||||||
Command::Task(task) => map.eval_task(task)?,
|
|
||||||
Command::Note(note) => map.eval_note(note)?,
|
|
||||||
Command::Birthday(birthday) => map.eval_birthday(birthday),
|
|
||||||
}
|
|
||||||
Ok(map.map.drain())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Files {
|
impl Files {
|
||||||
pub fn eval(&self, range: DateRange) -> Result<Vec<Entry>> {
|
pub fn eval(&self, mode: EntryMode, range: DateRange) -> Result<Vec<Entry>> {
|
||||||
let mut result = vec![];
|
let mut entries = Entries::new(mode, range);
|
||||||
for command in self.commands() {
|
for command in self.commands() {
|
||||||
result.append(&mut Eval::eval(range, &command)?);
|
for entry in CommandState::new(command, range).eval()?.entries() {
|
||||||
|
entries.add(entry);
|
||||||
}
|
}
|
||||||
Ok(result)
|
}
|
||||||
|
Ok(entries.entries())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
36
src/eval/command.rs
Normal file
36
src/eval/command.rs
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use chrono::NaiveDate;
|
||||||
|
|
||||||
|
use crate::files::SourcedCommand;
|
||||||
|
|
||||||
|
use super::{DateRange, Entry, Result};
|
||||||
|
|
||||||
|
pub struct CommandState<'a> {
|
||||||
|
command: SourcedCommand<'a>,
|
||||||
|
range: DateRange,
|
||||||
|
|
||||||
|
from: Option<NaiveDate>,
|
||||||
|
until: Option<NaiveDate>,
|
||||||
|
entries: HashMap<NaiveDate, Entry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> CommandState<'a> {
|
||||||
|
pub fn new(command: SourcedCommand<'a>, range: DateRange) -> Self {
|
||||||
|
Self {
|
||||||
|
range,
|
||||||
|
command,
|
||||||
|
from: None,
|
||||||
|
until: None,
|
||||||
|
entries: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn eval(self) -> Result<Self> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn entries(self) -> Vec<Entry> {
|
||||||
|
self.entries.into_values().collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/eval/error.rs
Normal file
23
src/eval/error.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
use std::result;
|
||||||
|
|
||||||
|
use chrono::NaiveDate;
|
||||||
|
|
||||||
|
use crate::files::commands::{Span, Time};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
DeltaInvalidStep {
|
||||||
|
span: Span,
|
||||||
|
start: NaiveDate,
|
||||||
|
start_time: Option<Time>,
|
||||||
|
prev: NaiveDate,
|
||||||
|
prev_time: Option<Time>,
|
||||||
|
},
|
||||||
|
DeltaNoTime {
|
||||||
|
span: Span,
|
||||||
|
start: NaiveDate,
|
||||||
|
prev: NaiveDate,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Result<T> = result::Result<T, Error>;
|
||||||
14
src/main.rs
14
src/main.rs
|
|
@ -8,10 +8,10 @@ use std::path::PathBuf;
|
||||||
use chrono::NaiveDate;
|
use chrono::NaiveDate;
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
// use crate::eval::DateRange;
|
use crate::eval::{DateRange, EntryMode};
|
||||||
use crate::files::Files;
|
use crate::files::Files;
|
||||||
|
|
||||||
// mod eval;
|
mod eval;
|
||||||
mod files;
|
mod files;
|
||||||
|
|
||||||
#[derive(Debug, StructOpt)]
|
#[derive(Debug, StructOpt)]
|
||||||
|
|
@ -26,11 +26,11 @@ 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(
|
let range = DateRange::new(
|
||||||
// NaiveDate::from_ymd(2021, 11, 20),
|
NaiveDate::from_ymd(2021, 11, 20),
|
||||||
// NaiveDate::from_ymd(2021, 11, 26),
|
NaiveDate::from_ymd(2021, 11, 26),
|
||||||
// );
|
);
|
||||||
// println!("{:#?}", files.eval(range));
|
println!("{:#?}", files.eval(EntryMode::Relevant, range));
|
||||||
|
|
||||||
files.mark_all_dirty();
|
files.mark_all_dirty();
|
||||||
files.save()?;
|
files.save()?;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue