Implement DATE statement

This commit is contained in:
Joscha 2021-12-05 18:27:42 +01:00
parent 85d2c4bf89
commit 4c83df76d4
6 changed files with 159 additions and 11 deletions

View file

@ -66,6 +66,22 @@ impl<'a> CommandState<'a> {
} }
} }
fn limit_from_until(&self, range: DateRange) -> Option<DateRange> {
let range_from = range.from();
let from = self
.from
.filter(|&from| from > range_from)
.unwrap_or(range_from);
let range_until = range.until();
let until = self
.until
.filter(|&until| until < range_until)
.unwrap_or(range_until);
DateRange::new(from, until)
}
/// Add an entry, respecting [`Self::from`] and [`Self::until`]. Does not /// Add an entry, respecting [`Self::from`] and [`Self::until`]. Does not
/// overwrite existing entries if a root date is specified. /// overwrite existing entries if a root date is specified.
fn add(&mut self, kind: EntryKind, dates: Option<Dates>) { fn add(&mut self, kind: EntryKind, dates: Option<Dates>) {

View file

@ -1,10 +1,12 @@
use chrono::NaiveDate; use chrono::NaiveDate;
use crate::files::commands::{self, Spanned, Time}; use crate::eval::date::Dates;
use crate::eval::DateRange;
use crate::files::commands::{self, Command, Spanned, Time};
use super::super::command::CommandState; use super::super::command::CommandState;
use super::super::delta::{Delta, DeltaStep}; use super::super::delta::{Delta, DeltaStep};
use super::super::Result; use super::super::{Error, Result};
pub struct DateSpec { pub struct DateSpec {
pub start: NaiveDate, pub start: NaiveDate,
@ -52,8 +54,77 @@ impl From<&commands::DateSpec> for DateSpec {
} }
} }
impl<'a> CommandState<'a> { impl DateSpec {
pub fn eval_date_spec(&mut self, spec: DateSpec) -> Result<()> { fn start_and_range(&self, s: &CommandState<'_>) -> Option<(NaiveDate, DateRange)> {
todo!() let (start, range) = match s.command.command {
Command::Task(_) => {
let last_done = s.last_done();
let start = last_done
.filter(|_| self.start_at_done)
.unwrap_or(self.start);
let range_from = last_done.map(|date| date.succ()).unwrap_or(self.start);
let range = s
.range
.expand_by(&self.end_delta)
.move_by(&self.start_delta)
.with_from(range_from)?;
(start, range)
}
Command::Note(_) => {
let start = self.start;
let range = s
.range
.expand_by(&self.end_delta)
.move_by(&self.start_delta);
(start, range)
}
};
let range = s.limit_from_until(range)?;
Some((start, range))
}
fn step(from: NaiveDate, repeat: &Delta) -> Result<NaiveDate> {
let to = repeat.apply_date(from)?;
if to > from {
Ok(to)
} else {
Err(Error::RepeatDidNotMoveForwards {
span: todo!(),
from,
to,
})
}
}
fn dates(&self, start: NaiveDate) -> Result<Dates> {
let root = self.start_delta.apply_date(start)?;
Ok(if let Some(root_time) = self.start_time {
let (other, other_time) = self.end_delta.apply_date_time(root, root_time)?;
Dates::new_with_time(root, root_time, other, other_time)
} else {
let other = self.end_delta.apply_date(root)?;
Dates::new(root, other)
})
}
}
impl<'a> CommandState<'a> {
pub fn eval_date_spec(&mut self, spec: DateSpec) -> Result<()> {
if let Some(repeat) = &spec.repeat {
if let Some((mut start, range)) = spec.start_and_range(self) {
while start < range.from() {
start = DateSpec::step(start, repeat)?;
}
while start <= range.until() {
let dates = spec.dates(start)?;
self.add(self.kind(), Some(dates));
start = DateSpec::step(start, repeat)?;
}
}
} else {
let dates = spec.dates(spec.start)?;
self.add(self.kind(), Some(dates));
}
Ok(())
} }
} }

View file

@ -266,11 +266,20 @@ impl Delta {
self.steps.iter().map(|step| step.value.upper_bound()).sum() self.steps.iter().map(|step| step.value.upper_bound()).sum()
} }
pub fn apply(&self, start: (NaiveDate, Option<Time>)) -> Result<(NaiveDate, Option<Time>)> { fn apply(&self, start: (NaiveDate, Option<Time>)) -> Result<(NaiveDate, Option<Time>)> {
let mut eval = DeltaEval::new(start.0, start.1); let mut eval = DeltaEval::new(start.0, start.1);
for step in &self.steps { for step in &self.steps {
eval.apply(step)?; eval.apply(step)?;
} }
Ok((eval.curr, eval.curr_time)) Ok((eval.curr, eval.curr_time))
} }
pub fn apply_date(&self, date: NaiveDate) -> Result<NaiveDate> {
Ok(self.apply((date, None))?.0)
}
pub fn apply_date_time(&self, date: NaiveDate, time: Time) -> Result<(NaiveDate, Time)> {
let (date, time) = self.apply((date, Some(time)))?;
Ok((date, time.expect("time was not preserved")))
}
} }

View file

@ -18,6 +18,14 @@ pub enum Error {
start: NaiveDate, start: NaiveDate,
prev: NaiveDate, prev: NaiveDate,
}, },
/// A `DATE`'s repeat delta did not move the date forwards in time. Instead,
/// it either remained at the current date (`to == from`) or moved backwards
/// in time (`to < from`).
RepeatDidNotMoveForwards {
span: Span,
from: NaiveDate,
to: NaiveDate,
},
/// A `MOVE a TO b` statement was executed, but there was no entry at the /// A `MOVE a TO b` statement was executed, but there was no entry at the
/// date `a`. /// date `a`.
MoveWithoutSource { span: Span }, MoveWithoutSource { span: Span },

View file

@ -1,6 +1,9 @@
use std::cmp;
use std::ops::RangeInclusive; use std::ops::RangeInclusive;
use chrono::{Datelike, NaiveDate}; use chrono::{Datelike, Duration, NaiveDate};
use super::delta::Delta;
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct DateRange { pub struct DateRange {
@ -9,9 +12,20 @@ pub struct DateRange {
} }
impl DateRange { impl DateRange {
pub fn new(from: NaiveDate, until: NaiveDate) -> Self { pub fn new(from: NaiveDate, until: NaiveDate) -> Option<Self> {
assert!(from <= until); if from <= until {
Self { from, until } Some(Self { from, until })
} else {
None
}
}
pub fn with_from(&self, from: NaiveDate) -> Option<Self> {
Self::new(from, self.until)
}
pub fn with_until(&self, until: NaiveDate) -> Option<Self> {
Self::new(self.from, until)
} }
pub fn contains(&self, date: NaiveDate) -> bool { pub fn contains(&self, date: NaiveDate) -> bool {
@ -29,4 +43,33 @@ impl DateRange {
pub fn years(&self) -> RangeInclusive<i32> { pub fn years(&self) -> RangeInclusive<i32> {
self.from.year()..=self.until.year() self.from.year()..=self.until.year()
} }
/// Expand the range so that it contains at least all dates from which the
/// original range could be reached using `delta`. This new range will
/// always contain the old range.
pub fn expand_by(&self, delta: &Delta) -> Self {
let expand_lower = cmp::min(-delta.upper_bound(), 0);
let expand_upper = cmp::max(-delta.lower_bound(), 0);
Self::new(
self.from + Duration::days(expand_lower.into()),
self.until + Duration::days(expand_upper.into()),
)
// The range is never shrunk, so the new range should always be valid.
.expect("expanded range shrunk")
}
/// Return a new range that contains at least all dates from which the
/// original range could be reached using `delta`. This new range might not
/// contain the old range.
pub fn move_by(&self, delta: &Delta) -> Self {
let move_lower = -delta.upper_bound();
let move_upper = -delta.lower_bound();
Self::new(
self.from + Duration::days(move_lower.into()),
self.until + Duration::days(move_upper.into()),
)
// The delta's upper bound is greater or equal than its lower bound, so
// the range should never become smaller. It can only move and expand.
.expect("moved range shrunk")
}
} }

View file

@ -29,7 +29,8 @@ fn main() -> anyhow::Result<()> {
let range = DateRange::new( let range = DateRange::new(
NaiveDate::from_ymd(2021, 1, 1), NaiveDate::from_ymd(2021, 1, 1),
NaiveDate::from_ymd(2021, 12, 31), NaiveDate::from_ymd(2021, 12, 31),
); )
.unwrap();
println!("{:#?}", files.eval(EntryMode::Relevant, range)); println!("{:#?}", files.eval(EntryMode::Relevant, range));
files.mark_all_dirty(); files.mark_all_dirty();