Adapt eval code to changes
These changes include the new file representation and Files API as well as the codespan error reporting.
This commit is contained in:
parent
810ec67cf7
commit
8ae691bc3c
8 changed files with 238 additions and 215 deletions
15
src/eval.rs
15
src/eval.rs
|
|
@ -1,14 +1,14 @@
|
|||
use chrono::NaiveDate;
|
||||
|
||||
use crate::files::arguments::{Range, RangeDate};
|
||||
use crate::files::Files;
|
||||
use crate::files::{FileSource, Files};
|
||||
|
||||
use self::command::CommandState;
|
||||
use self::command::{CommandState, EvalCommand};
|
||||
pub use self::date::Dates;
|
||||
use self::delta::Delta;
|
||||
use self::entry::Entries;
|
||||
pub use self::entry::{Entry, EntryKind, EntryMode};
|
||||
pub use self::error::{Error, Result, SourceInfo};
|
||||
pub use self::error::Error;
|
||||
pub use self::range::DateRange;
|
||||
|
||||
mod command;
|
||||
|
|
@ -20,19 +20,22 @@ mod range;
|
|||
mod util;
|
||||
|
||||
impl Files {
|
||||
pub fn eval(&self, mode: EntryMode, range: DateRange) -> Result<Vec<Entry>> {
|
||||
pub fn eval(&self, mode: EntryMode, range: DateRange) -> Result<Vec<Entry>, Error<FileSource>> {
|
||||
let mut entries = Entries::new(mode, range);
|
||||
for command in self.commands() {
|
||||
for entry in CommandState::new(command, range).eval()?.entries() {
|
||||
let source = command.source;
|
||||
if let Some(command) = EvalCommand::new(command.command) {
|
||||
for entry in CommandState::new(command, source, range).eval()?.entries() {
|
||||
entries.add(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(entries.entries())
|
||||
}
|
||||
}
|
||||
|
||||
impl Range {
|
||||
pub fn eval(&self, index: usize, today: NaiveDate) -> Result<DateRange> {
|
||||
pub fn eval<S: Copy>(&self, index: S, today: NaiveDate) -> Result<DateRange, Error<S>> {
|
||||
let mut start = match self.start {
|
||||
RangeDate::Date(d) => d,
|
||||
RangeDate::Today => today,
|
||||
|
|
|
|||
|
|
@ -6,18 +6,69 @@ use crate::files::commands::{
|
|||
self, BirthdaySpec, Command, Done, DoneDate, DoneKind, Note, Spec, Statement, Task,
|
||||
};
|
||||
use crate::files::primitives::{Span, Spanned, Time};
|
||||
use crate::files::SourcedCommand;
|
||||
use crate::files::{FileSource, Source};
|
||||
|
||||
use super::date::Dates;
|
||||
use super::delta::Delta;
|
||||
use super::{DateRange, Entry, EntryKind, Error, Result};
|
||||
use super::{DateRange, Entry, EntryKind, Error};
|
||||
|
||||
mod birthday;
|
||||
mod date;
|
||||
mod formula;
|
||||
|
||||
/// A command that can be evaluated.
|
||||
pub enum EvalCommand<'a> {
|
||||
Task(&'a Task),
|
||||
Note(&'a Note),
|
||||
}
|
||||
|
||||
impl<'a> EvalCommand<'a> {
|
||||
pub fn new(command: &'a Command) -> Option<Self> {
|
||||
match command {
|
||||
Command::Task(task) => Some(Self::Task(task)),
|
||||
Command::Note(note) => Some(Self::Note(note)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn statements(&self) -> &[Statement] {
|
||||
match self {
|
||||
Self::Task(task) => &task.statements,
|
||||
Self::Note(note) => ¬e.statements,
|
||||
}
|
||||
}
|
||||
|
||||
fn kind(&self) -> EntryKind {
|
||||
match self {
|
||||
Self::Task(_) => EntryKind::Task,
|
||||
Self::Note(_) => EntryKind::Note,
|
||||
}
|
||||
}
|
||||
|
||||
/// Last root date mentioned in any `DONE`.
|
||||
fn last_done_root(&self) -> Option<NaiveDate> {
|
||||
match self {
|
||||
Self::Task(task) => task
|
||||
.done
|
||||
.iter()
|
||||
.filter_map(|done| done.date.map(DoneDate::root))
|
||||
.max(),
|
||||
Self::Note(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Last completion date mentioned in any `DONE`.
|
||||
fn last_done_completion(&self) -> Option<NaiveDate> {
|
||||
match self {
|
||||
Self::Task(task) => task.done.iter().map(|done| done.done_at).max(),
|
||||
Self::Note(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CommandState<'a> {
|
||||
command: SourcedCommand<'a>,
|
||||
command: EvalCommand<'a>,
|
||||
source: Source,
|
||||
range: DateRange,
|
||||
|
||||
from: Option<NaiveDate>,
|
||||
|
|
@ -29,7 +80,7 @@ pub struct CommandState<'a> {
|
|||
}
|
||||
|
||||
impl<'a> CommandState<'a> {
|
||||
pub fn new(command: SourcedCommand<'a>, mut range: DateRange) -> Self {
|
||||
pub fn new(command: EvalCommand<'a>, source: Source, mut range: DateRange) -> Self {
|
||||
// If we don't calculate entries for the source of the move command, it
|
||||
// fails even though the user did nothing wrong. Also, move commands (or
|
||||
// chains thereof) may move an initially out-of-range entry into range.
|
||||
|
|
@ -37,15 +88,16 @@ impl<'a> CommandState<'a> {
|
|||
// To fix this, we just expand the range to contain all move command
|
||||
// sources. This is a quick fix, but until it becomes a performance
|
||||
// issue (if ever), it's probably fine.
|
||||
for statement in command.command.statements() {
|
||||
for statement in command.statements() {
|
||||
if let Statement::Move { from, .. } = statement {
|
||||
range = range.containing(*from)
|
||||
}
|
||||
}
|
||||
|
||||
Self {
|
||||
range,
|
||||
command,
|
||||
source,
|
||||
range,
|
||||
from: None,
|
||||
until: None,
|
||||
remind: None,
|
||||
|
|
@ -54,10 +106,10 @@ impl<'a> CommandState<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn eval(mut self) -> Result<Self> {
|
||||
match self.command.command {
|
||||
Command::Task(task) => self.eval_task(task)?,
|
||||
Command::Note(note) => self.eval_note(note)?,
|
||||
pub fn eval(mut self) -> Result<Self, Error<FileSource>> {
|
||||
match self.command {
|
||||
EvalCommand::Task(task) => self.eval_task(task)?,
|
||||
EvalCommand::Note(note) => self.eval_note(note)?,
|
||||
}
|
||||
Ok(self)
|
||||
}
|
||||
|
|
@ -71,13 +123,6 @@ impl<'a> CommandState<'a> {
|
|||
|
||||
// Helper functions
|
||||
|
||||
fn kind(&self) -> EntryKind {
|
||||
match self.command.command {
|
||||
Command::Task(_) => EntryKind::Task,
|
||||
Command::Note(_) => EntryKind::Note,
|
||||
}
|
||||
}
|
||||
|
||||
fn range_with_remind(&self) -> DateRange {
|
||||
match &self.remind {
|
||||
None => self.range,
|
||||
|
|
@ -85,26 +130,6 @@ impl<'a> CommandState<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Last root date mentioned in any `DONE`.
|
||||
fn last_done_root(&self) -> Option<NaiveDate> {
|
||||
match self.command.command {
|
||||
Command::Task(task) => task
|
||||
.done
|
||||
.iter()
|
||||
.filter_map(|done| done.date.map(DoneDate::root))
|
||||
.max(),
|
||||
Command::Note(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Last completion date mentioned in any `DONE`.
|
||||
fn last_done_completion(&self) -> Option<NaiveDate> {
|
||||
match self.command.command {
|
||||
Command::Task(task) => task.done.iter().map(|done| done.done_at).max(),
|
||||
Command::Note(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn limit_from_until(&self, range: DateRange) -> Option<DateRange> {
|
||||
let range_from = range.from();
|
||||
let from = self
|
||||
|
|
@ -125,9 +150,13 @@ impl<'a> CommandState<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn entry_with_remind(&self, kind: EntryKind, dates: Option<Dates>) -> Result<Entry> {
|
||||
fn entry_with_remind(
|
||||
&self,
|
||||
kind: EntryKind,
|
||||
dates: Option<Dates>,
|
||||
) -> Result<Entry, Error<FileSource>> {
|
||||
let remind = if let (Some(dates), Some(delta)) = (dates, &self.remind) {
|
||||
let index = self.command.source.file();
|
||||
let index = self.source.file();
|
||||
let start = dates.sorted().root();
|
||||
let remind = delta.value.apply_date(index, dates.sorted().root())?;
|
||||
if remind >= start {
|
||||
|
|
@ -143,7 +172,7 @@ impl<'a> CommandState<'a> {
|
|||
None
|
||||
};
|
||||
|
||||
Ok(Entry::new(self.command.source, kind, dates, remind))
|
||||
Ok(Entry::new(self.source, kind, dates, remind))
|
||||
}
|
||||
|
||||
/// Add an entry, respecting [`Self::from`] and [`Self::until`]. Does not
|
||||
|
|
@ -185,13 +214,13 @@ impl<'a> CommandState<'a> {
|
|||
.any(|s| matches!(s, Statement::Date(_) | Statement::BDate(_)))
|
||||
}
|
||||
|
||||
fn eval_task(&mut self, task: &Task) -> Result<()> {
|
||||
fn eval_task(&mut self, task: &Task) -> Result<(), Error<FileSource>> {
|
||||
if Self::has_date_stmt(&task.statements) {
|
||||
for statement in &task.statements {
|
||||
self.eval_statement(statement)?;
|
||||
}
|
||||
} else if task.done.is_empty() {
|
||||
self.add(self.entry_with_remind(self.kind(), None)?);
|
||||
self.add(self.entry_with_remind(self.command.kind(), None)?);
|
||||
}
|
||||
|
||||
for done in &task.done {
|
||||
|
|
@ -201,19 +230,19 @@ impl<'a> CommandState<'a> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn eval_note(&mut self, note: &Note) -> Result<()> {
|
||||
fn eval_note(&mut self, note: &Note) -> Result<(), Error<FileSource>> {
|
||||
if Self::has_date_stmt(¬e.statements) {
|
||||
for statement in ¬e.statements {
|
||||
self.eval_statement(statement)?;
|
||||
}
|
||||
} else {
|
||||
self.add(self.entry_with_remind(self.kind(), None)?);
|
||||
self.add(self.entry_with_remind(self.command.kind(), None)?);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn eval_statement(&mut self, statement: &Statement) -> Result<()> {
|
||||
fn eval_statement(&mut self, statement: &Statement) -> Result<(), Error<FileSource>> {
|
||||
match statement {
|
||||
Statement::Date(spec) => self.eval_date(spec)?,
|
||||
Statement::BDate(spec) => self.eval_bdate(spec)?,
|
||||
|
|
@ -231,7 +260,7 @@ impl<'a> CommandState<'a> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn eval_date(&mut self, spec: &Spec) -> Result<()> {
|
||||
fn eval_date(&mut self, spec: &Spec) -> Result<(), Error<FileSource>> {
|
||||
match spec {
|
||||
Spec::Date(spec) => self.eval_date_spec(spec.into()),
|
||||
Spec::Weekday(spec) => self.eval_formula_spec(spec.into()),
|
||||
|
|
@ -239,7 +268,7 @@ impl<'a> CommandState<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn eval_bdate(&mut self, spec: &BirthdaySpec) -> Result<()> {
|
||||
fn eval_bdate(&mut self, spec: &BirthdaySpec) -> Result<(), Error<FileSource>> {
|
||||
self.eval_birthday_spec(spec)
|
||||
}
|
||||
|
||||
|
|
@ -254,7 +283,7 @@ impl<'a> CommandState<'a> {
|
|||
from: NaiveDate,
|
||||
to: Option<NaiveDate>,
|
||||
to_time: Option<Spanned<Time>>,
|
||||
) -> Result<()> {
|
||||
) -> Result<(), Error<FileSource>> {
|
||||
if let Some(mut entry) = self.dated.remove(&from) {
|
||||
let mut dates = entry.dates.expect("comes from self.dated");
|
||||
|
||||
|
|
@ -268,7 +297,7 @@ impl<'a> CommandState<'a> {
|
|||
delta = delta + Duration::minutes(root.minutes_to(to_time.value));
|
||||
} else {
|
||||
return Err(Error::TimedMoveWithoutTime {
|
||||
index: self.command.source.file(),
|
||||
index: self.source.file(),
|
||||
span: to_time.span,
|
||||
});
|
||||
}
|
||||
|
|
@ -281,7 +310,7 @@ impl<'a> CommandState<'a> {
|
|||
Ok(())
|
||||
} else {
|
||||
Err(Error::MoveWithoutSource {
|
||||
index: self.command.source.file(),
|
||||
index: self.source.file(),
|
||||
span,
|
||||
})
|
||||
}
|
||||
|
|
@ -295,7 +324,7 @@ impl<'a> CommandState<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn eval_done(&mut self, done: &Done) -> Result<()> {
|
||||
fn eval_done(&mut self, done: &Done) -> Result<(), Error<FileSource>> {
|
||||
let kind = match done.kind {
|
||||
DoneKind::Done => EntryKind::TaskDone(done.done_at),
|
||||
DoneKind::Canceled => EntryKind::TaskCanceled(done.done_at),
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
use chrono::{Datelike, NaiveDate};
|
||||
|
||||
use crate::files::commands::BirthdaySpec;
|
||||
use crate::files::FileSource;
|
||||
|
||||
use super::super::command::CommandState;
|
||||
use super::super::date::Dates;
|
||||
use super::super::error::Result;
|
||||
use super::super::error::Error;
|
||||
use super::super::EntryKind;
|
||||
|
||||
impl<'a> CommandState<'a> {
|
||||
pub fn eval_birthday_spec(&mut self, spec: &BirthdaySpec) -> Result<()> {
|
||||
pub fn eval_birthday_spec(&mut self, spec: &BirthdaySpec) -> Result<(), Error<FileSource>> {
|
||||
let range = match self.limit_from_until(self.range_with_remind()) {
|
||||
Some(range) => range,
|
||||
None => return Ok(()),
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
use chrono::NaiveDate;
|
||||
|
||||
use crate::files::commands::{self, Command};
|
||||
use crate::files::commands;
|
||||
use crate::files::primitives::{Spanned, Time};
|
||||
use crate::files::FileSource;
|
||||
|
||||
use super::super::command::CommandState;
|
||||
use super::super::date::Dates;
|
||||
use super::super::delta::{Delta, DeltaStep};
|
||||
use super::super::{DateRange, Error, Result};
|
||||
use super::super::{DateRange, Error};
|
||||
use super::EvalCommand;
|
||||
|
||||
pub struct DateSpec {
|
||||
pub start: NaiveDate,
|
||||
|
|
@ -72,14 +74,16 @@ impl DateSpec {
|
|||
/// `start` date itself should be skipped (and thus not result in an entry).
|
||||
/// This may be necessary if [`Self::start_at_done`] is set.
|
||||
fn start_and_range(&self, s: &CommandState<'_>) -> Option<(NaiveDate, bool, DateRange)> {
|
||||
let (start, skip, range) = match s.command.command {
|
||||
Command::Task(_) => {
|
||||
let (start, skip, range) = match s.command {
|
||||
EvalCommand::Task(_) => {
|
||||
let (start, skip) = s
|
||||
.command
|
||||
.last_done_completion()
|
||||
.map(|start| (start, true))
|
||||
.filter(|_| self.start_at_done)
|
||||
.unwrap_or((self.start, false));
|
||||
let range_from = s
|
||||
.command
|
||||
.last_done_root()
|
||||
.map(|date| date.succ())
|
||||
.unwrap_or(self.start);
|
||||
|
|
@ -90,7 +94,7 @@ impl DateSpec {
|
|||
.with_from(range_from)?;
|
||||
(start, skip, range)
|
||||
}
|
||||
Command::Note(_) => {
|
||||
EvalCommand::Note(_) => {
|
||||
let start = self.start;
|
||||
let range = s
|
||||
.range_with_remind()
|
||||
|
|
@ -103,7 +107,11 @@ impl DateSpec {
|
|||
Some((start, skip, range))
|
||||
}
|
||||
|
||||
fn step(index: usize, from: NaiveDate, repeat: &Spanned<Delta>) -> Result<NaiveDate> {
|
||||
fn step(
|
||||
index: FileSource,
|
||||
from: NaiveDate,
|
||||
repeat: &Spanned<Delta>,
|
||||
) -> Result<NaiveDate, Error<FileSource>> {
|
||||
let to = repeat.value.apply_date(index, from)?;
|
||||
if to > from {
|
||||
Ok(to)
|
||||
|
|
@ -117,7 +125,7 @@ impl DateSpec {
|
|||
}
|
||||
}
|
||||
|
||||
fn dates(&self, index: usize, start: NaiveDate) -> Result<Dates> {
|
||||
fn dates(&self, index: FileSource, start: NaiveDate) -> Result<Dates, Error<FileSource>> {
|
||||
let root = self.start_delta.apply_date(index, start)?;
|
||||
Ok(if let Some(root_time) = self.start_time {
|
||||
let (other, other_time) = self.end_delta.apply_date_time(index, root, root_time)?;
|
||||
|
|
@ -130,8 +138,8 @@ impl DateSpec {
|
|||
}
|
||||
|
||||
impl<'a> CommandState<'a> {
|
||||
pub fn eval_date_spec(&mut self, spec: DateSpec) -> Result<()> {
|
||||
let index = self.command.source.file();
|
||||
pub fn eval_date_spec(&mut self, spec: DateSpec) -> Result<(), Error<FileSource>> {
|
||||
let index = self.source.file();
|
||||
if let Some(repeat) = &spec.repeat {
|
||||
if let Some((mut start, skip, range)) = spec.start_and_range(self) {
|
||||
if skip {
|
||||
|
|
@ -142,13 +150,13 @@ impl<'a> CommandState<'a> {
|
|||
}
|
||||
while start <= range.until() {
|
||||
let dates = spec.dates(index, start)?;
|
||||
self.add(self.entry_with_remind(self.kind(), Some(dates))?);
|
||||
self.add(self.entry_with_remind(self.command.kind(), Some(dates))?);
|
||||
start = DateSpec::step(index, start, repeat)?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let dates = spec.dates(index, spec.start)?;
|
||||
self.add(self.entry_with_remind(self.kind(), Some(dates))?);
|
||||
self.add(self.entry_with_remind(self.command.kind(), Some(dates))?);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
use chrono::{Datelike, Duration, NaiveDate};
|
||||
|
||||
use crate::files::commands::{self, Command};
|
||||
use crate::files::commands;
|
||||
use crate::files::primitives::{Span, Spanned, Time, Weekday};
|
||||
use crate::files::FileSource;
|
||||
|
||||
use super::super::command::CommandState;
|
||||
use super::super::date::Dates;
|
||||
use super::super::delta::{Delta, DeltaStep};
|
||||
use super::super::{util, DateRange, Error, Result};
|
||||
use super::super::{util, DateRange, Error};
|
||||
use super::EvalCommand;
|
||||
|
||||
fn b2i(b: bool) -> i64 {
|
||||
if b {
|
||||
|
|
@ -47,7 +49,7 @@ pub enum Var {
|
|||
}
|
||||
|
||||
impl Var {
|
||||
fn eval(self, index: usize, date: NaiveDate) -> Result<i64> {
|
||||
fn eval<S>(self, index: S, date: NaiveDate) -> Result<i64, Error<S>> {
|
||||
Ok(match self {
|
||||
Var::JulianDay => date.num_days_from_ce().into(),
|
||||
Var::Year => date.year().into(),
|
||||
|
|
@ -202,7 +204,7 @@ impl From<Weekday> for Expr {
|
|||
}
|
||||
|
||||
impl Expr {
|
||||
fn eval(&self, index: usize, date: NaiveDate) -> Result<i64> {
|
||||
fn eval<S: Copy>(&self, index: S, date: NaiveDate) -> Result<i64, Error<S>> {
|
||||
Ok(match self {
|
||||
Expr::Lit(l) => *l,
|
||||
Expr::Var(v) => v.eval(index, date)?,
|
||||
|
|
@ -328,12 +330,12 @@ impl FormulaSpec {
|
|||
.expand_by(&self.end_delta)
|
||||
.move_by(&self.start_delta);
|
||||
|
||||
if let Command::Task(_) = s.command.command {
|
||||
if let Some(last_done_root) = s.last_done_root() {
|
||||
if let EvalCommand::Task(_) = s.command {
|
||||
if let Some(last_done_root) = s.command.last_done_root() {
|
||||
range = range.with_from(last_done_root.succ())?;
|
||||
} else if let Some(from) = s.from {
|
||||
range = range.with_from(from)?;
|
||||
} else if matches!(s.command.command, Command::Task(_)) {
|
||||
} else if matches!(s.command, EvalCommand::Task(_)) {
|
||||
// We have no idea if we missed any tasks since the user hasn't
|
||||
// specified a `FROM`, so we just just look back one year. Any
|
||||
// task older than a year is probably not important anyways...
|
||||
|
|
@ -344,7 +346,7 @@ impl FormulaSpec {
|
|||
s.limit_from_until(range)
|
||||
}
|
||||
|
||||
fn dates(&self, index: usize, start: NaiveDate) -> Result<Dates> {
|
||||
fn dates(&self, index: FileSource, start: NaiveDate) -> Result<Dates, Error<FileSource>> {
|
||||
let root = self.start_delta.apply_date(index, start)?;
|
||||
Ok(if let Some(root_time) = self.start_time {
|
||||
let (other, other_time) = self.end_delta.apply_date_time(index, root, root_time)?;
|
||||
|
|
@ -355,19 +357,19 @@ impl FormulaSpec {
|
|||
})
|
||||
}
|
||||
|
||||
fn eval(&self, index: usize, date: NaiveDate) -> Result<bool> {
|
||||
fn eval(&self, index: FileSource, date: NaiveDate) -> Result<bool, Error<FileSource>> {
|
||||
Ok(i2b(self.start.eval(index, date)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> CommandState<'a> {
|
||||
pub fn eval_formula_spec(&mut self, spec: FormulaSpec) -> Result<()> {
|
||||
pub fn eval_formula_spec(&mut self, spec: FormulaSpec) -> Result<(), Error<FileSource>> {
|
||||
if let Some(range) = spec.range(self) {
|
||||
let index = self.command.source.file();
|
||||
let index = self.source.file();
|
||||
for day in range.days() {
|
||||
if spec.eval(index, day)? {
|
||||
let dates = spec.dates(index, day)?;
|
||||
self.add(self.entry_with_remind(self.kind(), Some(dates))?);
|
||||
self.add(self.entry_with_remind(self.command.kind(), Some(dates))?);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -386,7 +388,7 @@ mod tests {
|
|||
use super::{Expr, Var};
|
||||
|
||||
fn expr(expr: &Expr, date: NaiveDate, target: i64) {
|
||||
if let Ok(result) = expr.eval(0, date) {
|
||||
if let Ok(result) = expr.eval((), date) {
|
||||
assert_eq!(result, target);
|
||||
} else {
|
||||
panic!("formula produced error for day {}", date);
|
||||
|
|
@ -400,7 +402,7 @@ mod tests {
|
|||
for delta in -1000..1000 {
|
||||
let d1 = NaiveDate::from_ymd(2021, 12, 19);
|
||||
let d2 = d1 + Duration::days(delta);
|
||||
assert_eq!(e.eval(0, d2).unwrap() - e.eval(0, d1).unwrap(), delta);
|
||||
assert_eq!(e.eval((), d2).unwrap() - e.eval((), d1).unwrap(), delta);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use chrono::{Datelike, Duration, NaiveDate};
|
|||
use crate::files::commands;
|
||||
use crate::files::primitives::{Span, Spanned, Time, Weekday};
|
||||
|
||||
use super::{util, Error, Result};
|
||||
use super::{util, Error};
|
||||
|
||||
/// Like [`commands::DeltaStep`] but includes a new constructor,
|
||||
/// [`DeltaStep::Time`].
|
||||
|
|
@ -142,16 +142,16 @@ impl From<&commands::Delta> for Delta {
|
|||
}
|
||||
}
|
||||
|
||||
struct DeltaEval {
|
||||
index: usize,
|
||||
struct DeltaEval<I> {
|
||||
index: I,
|
||||
start: NaiveDate,
|
||||
start_time: Option<Time>,
|
||||
curr: NaiveDate,
|
||||
curr_time: Option<Time>,
|
||||
}
|
||||
|
||||
impl DeltaEval {
|
||||
fn new(index: usize, start: NaiveDate, start_time: Option<Time>) -> Self {
|
||||
impl<S: Copy> DeltaEval<S> {
|
||||
fn new(index: S, start: NaiveDate, start_time: Option<Time>) -> Self {
|
||||
Self {
|
||||
index,
|
||||
start,
|
||||
|
|
@ -161,7 +161,7 @@ impl DeltaEval {
|
|||
}
|
||||
}
|
||||
|
||||
fn err_step(&self, span: Span) -> Error {
|
||||
fn err_step(&self, span: Span) -> Error<S> {
|
||||
Error::DeltaInvalidStep {
|
||||
index: self.index,
|
||||
span,
|
||||
|
|
@ -172,7 +172,7 @@ impl DeltaEval {
|
|||
}
|
||||
}
|
||||
|
||||
fn err_time(&self, span: Span) -> Error {
|
||||
fn err_time(&self, span: Span) -> Error<S> {
|
||||
Error::DeltaNoTime {
|
||||
index: self.index,
|
||||
span,
|
||||
|
|
@ -181,7 +181,7 @@ impl DeltaEval {
|
|||
}
|
||||
}
|
||||
|
||||
fn apply(&mut self, step: &Spanned<DeltaStep>) -> Result<()> {
|
||||
fn apply(&mut self, step: &Spanned<DeltaStep>) -> Result<(), Error<S>> {
|
||||
match step.value {
|
||||
DeltaStep::Year(n) => self.step_year(step.span, n)?,
|
||||
DeltaStep::Month(n) => self.step_month(step.span, n)?,
|
||||
|
|
@ -196,7 +196,7 @@ impl DeltaEval {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn step_year(&mut self, span: Span, amount: i32) -> Result<()> {
|
||||
fn step_year(&mut self, span: Span, amount: i32) -> Result<(), Error<S>> {
|
||||
let year = self.curr.year() + amount;
|
||||
match NaiveDate::from_ymd_opt(year, self.curr.month(), self.curr.day()) {
|
||||
None => Err(self.err_step(span)),
|
||||
|
|
@ -207,7 +207,7 @@ impl DeltaEval {
|
|||
}
|
||||
}
|
||||
|
||||
fn step_month(&mut self, span: Span, amount: i32) -> Result<()> {
|
||||
fn step_month(&mut self, span: Span, amount: i32) -> Result<(), Error<S>> {
|
||||
let (year, month) = util::add_months(self.curr.year(), self.curr.month(), amount);
|
||||
match NaiveDate::from_ymd_opt(year, month, self.curr.day()) {
|
||||
None => Err(self.err_step(span)),
|
||||
|
|
@ -218,7 +218,7 @@ impl DeltaEval {
|
|||
}
|
||||
}
|
||||
|
||||
fn step_month_reverse(&mut self, span: Span, amount: i32) -> Result<()> {
|
||||
fn step_month_reverse(&mut self, span: Span, amount: i32) -> Result<(), Error<S>> {
|
||||
// Calculate offset from the last day of the month
|
||||
let month_length = util::month_length(self.curr.year(), self.curr.month()) as i32;
|
||||
let end_offset = self.curr.day() as i32 - month_length;
|
||||
|
|
@ -252,7 +252,7 @@ impl DeltaEval {
|
|||
self.curr += delta;
|
||||
}
|
||||
|
||||
fn step_hour(&mut self, span: Span, amount: i32) -> Result<()> {
|
||||
fn step_hour(&mut self, span: Span, amount: i32) -> Result<(), Error<S>> {
|
||||
let time = match self.curr_time {
|
||||
Some(time) => time,
|
||||
None => return Err(self.err_time(span)),
|
||||
|
|
@ -264,7 +264,7 @@ impl DeltaEval {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn step_minute(&mut self, span: Span, amount: i32) -> Result<()> {
|
||||
fn step_minute(&mut self, span: Span, amount: i32) -> Result<(), Error<S>> {
|
||||
let time = match self.curr_time {
|
||||
Some(time) => time,
|
||||
None => return Err(self.err_time(span)),
|
||||
|
|
@ -290,7 +290,7 @@ impl DeltaEval {
|
|||
}
|
||||
}
|
||||
|
||||
fn step_time(&mut self, span: Span, time: Time) -> Result<()> {
|
||||
fn step_time(&mut self, span: Span, time: Time) -> Result<(), Error<S>> {
|
||||
let curr_time = match self.curr_time {
|
||||
Some(time) => time,
|
||||
None => return Err(self.err_time(span)),
|
||||
|
|
@ -313,11 +313,11 @@ impl Delta {
|
|||
self.steps.iter().map(|step| step.value.upper_bound()).sum()
|
||||
}
|
||||
|
||||
fn apply(
|
||||
fn apply<S: Copy>(
|
||||
&self,
|
||||
index: usize,
|
||||
index: S,
|
||||
start: (NaiveDate, Option<Time>),
|
||||
) -> Result<(NaiveDate, Option<Time>)> {
|
||||
) -> Result<(NaiveDate, Option<Time>), Error<S>> {
|
||||
let mut eval = DeltaEval::new(index, start.0, start.1);
|
||||
for step in &self.steps {
|
||||
eval.apply(step)?;
|
||||
|
|
@ -325,16 +325,16 @@ impl Delta {
|
|||
Ok((eval.curr, eval.curr_time))
|
||||
}
|
||||
|
||||
pub fn apply_date(&self, index: usize, date: NaiveDate) -> Result<NaiveDate> {
|
||||
pub fn apply_date<S: Copy>(&self, index: S, date: NaiveDate) -> Result<NaiveDate, Error<S>> {
|
||||
Ok(self.apply(index, (date, None))?.0)
|
||||
}
|
||||
|
||||
pub fn apply_date_time(
|
||||
pub fn apply_date_time<S: Copy>(
|
||||
&self,
|
||||
index: usize,
|
||||
index: S,
|
||||
date: NaiveDate,
|
||||
time: Time,
|
||||
) -> Result<(NaiveDate, Time)> {
|
||||
) -> Result<(NaiveDate, Time), Error<S>> {
|
||||
let (date, time) = self.apply(index, (date, Some(time)))?;
|
||||
Ok((date, time.expect("time was not preserved")))
|
||||
}
|
||||
|
|
@ -346,7 +346,7 @@ mod tests {
|
|||
|
||||
use crate::files::primitives::{Span, Spanned, Time};
|
||||
|
||||
use super::super::Result;
|
||||
use super::super::Error;
|
||||
use super::{Delta, DeltaStep as Step};
|
||||
|
||||
const SPAN: Span = Span { start: 12, end: 34 };
|
||||
|
|
@ -357,8 +357,8 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
fn apply_d(step: Step, from: (i32, u32, u32)) -> Result<NaiveDate> {
|
||||
delta(step).apply_date(0, NaiveDate::from_ymd(from.0, from.1, from.2))
|
||||
fn apply_d(step: Step, from: (i32, u32, u32)) -> Result<NaiveDate, Error<()>> {
|
||||
delta(step).apply_date((), NaiveDate::from_ymd(from.0, from.1, from.2))
|
||||
}
|
||||
|
||||
fn test_d(step: Step, from: (i32, u32, u32), expected: (i32, u32, u32)) {
|
||||
|
|
@ -368,9 +368,12 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
fn apply_dt(step: Step, from: (i32, u32, u32, u32, u32)) -> Result<(NaiveDate, Time)> {
|
||||
fn apply_dt(
|
||||
step: Step,
|
||||
from: (i32, u32, u32, u32, u32),
|
||||
) -> Result<(NaiveDate, Time), Error<()>> {
|
||||
delta(step).apply_date_time(
|
||||
0,
|
||||
(),
|
||||
NaiveDate::from_ymd(from.0, from.1, from.2),
|
||||
Time::new(from.3, from.4),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
use std::result;
|
||||
|
||||
use chrono::NaiveDate;
|
||||
use codespan_reporting::diagnostic::{Diagnostic, Label};
|
||||
|
||||
use crate::files::primitives::{Span, Time};
|
||||
use crate::files::{FileSource, Files};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
pub enum Error<S> {
|
||||
/// A delta step resulted in an invalid date.
|
||||
#[error("delta step resulted in invalid date")]
|
||||
DeltaInvalidStep {
|
||||
index: usize,
|
||||
index: S,
|
||||
span: Span,
|
||||
start: NaiveDate,
|
||||
start_time: Option<Time>,
|
||||
|
|
@ -19,7 +19,7 @@ pub enum Error {
|
|||
/// A time-based delta step was applied to a date without time.
|
||||
#[error("time-based delta step applied to date without time")]
|
||||
DeltaNoTime {
|
||||
index: usize,
|
||||
index: S,
|
||||
span: Span,
|
||||
start: NaiveDate,
|
||||
prev: NaiveDate,
|
||||
|
|
@ -29,7 +29,7 @@ pub enum Error {
|
|||
/// in time (`to < from`).
|
||||
#[error("repeat delta did not move forwards")]
|
||||
RepeatDidNotMoveForwards {
|
||||
index: usize,
|
||||
index: S,
|
||||
span: Span,
|
||||
from: NaiveDate,
|
||||
to: NaiveDate,
|
||||
|
|
@ -39,7 +39,7 @@ pub enum Error {
|
|||
/// moved forwards in time (`from < to`).
|
||||
#[error("remind delta did not move backwards")]
|
||||
RemindDidNotMoveBackwards {
|
||||
index: usize,
|
||||
index: S,
|
||||
span: Span,
|
||||
from: NaiveDate,
|
||||
to: NaiveDate,
|
||||
|
|
@ -47,55 +47,36 @@ pub enum Error {
|
|||
/// A `MOVE a TO b` statement was executed, but there was no entry at the
|
||||
/// date `a`.
|
||||
#[error("tried to move nonexisting entry")]
|
||||
MoveWithoutSource { index: usize, span: Span },
|
||||
MoveWithoutSource { index: S, span: Span },
|
||||
/// A `MOVE a TO b` statement was executed where `b` contains a time but `a`
|
||||
/// doesn't was executed.
|
||||
#[error("tried to move un-timed entry to new time")]
|
||||
TimedMoveWithoutTime { index: usize, span: Span },
|
||||
TimedMoveWithoutTime { index: S, span: Span },
|
||||
/// A division by zero has occurred.
|
||||
#[error("tried to divide by zero")]
|
||||
DivByZero {
|
||||
index: usize,
|
||||
index: S,
|
||||
span: Span,
|
||||
date: NaiveDate,
|
||||
},
|
||||
/// A modulo operation by zero has occurred.
|
||||
#[error("tried to modulo by zero")]
|
||||
ModByZero {
|
||||
index: usize,
|
||||
index: S,
|
||||
span: Span,
|
||||
date: NaiveDate,
|
||||
},
|
||||
/// Easter calculation failed.
|
||||
#[error("easter calculation failed")]
|
||||
Easter {
|
||||
index: usize,
|
||||
index: S,
|
||||
span: Span,
|
||||
date: NaiveDate,
|
||||
msg: &'static str,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct SourceInfo<'a> {
|
||||
pub name: Option<String>,
|
||||
pub content: &'a str,
|
||||
}
|
||||
|
||||
impl Error {
|
||||
fn print_at<'a>(sources: &[SourceInfo<'a>], index: &usize, span: &Span, message: String) {
|
||||
use pest::error as pe;
|
||||
|
||||
let source = sources.get(*index).expect("index is valid");
|
||||
let span = pest::Span::new(source.content, span.start, span.end).expect("span is valid");
|
||||
let variant = pe::ErrorVariant::<()>::CustomError { message };
|
||||
let mut error = pe::Error::new_from_span(variant, span);
|
||||
if let Some(name) = &source.name {
|
||||
error = error.with_path(name);
|
||||
}
|
||||
|
||||
eprintln!("{}", error);
|
||||
}
|
||||
|
||||
impl Error<FileSource> {
|
||||
fn fmt_date_time(date: NaiveDate, time: Option<Time>) -> String {
|
||||
match time {
|
||||
None => format!("{}", date),
|
||||
|
|
@ -103,8 +84,8 @@ impl Error {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn print<'a>(&self, sources: &[SourceInfo<'a>]) {
|
||||
match self {
|
||||
pub fn print(&self, files: &Files) {
|
||||
let diagnostic = match self {
|
||||
Error::DeltaInvalidStep {
|
||||
index,
|
||||
span,
|
||||
|
|
@ -113,95 +94,91 @@ impl Error {
|
|||
prev,
|
||||
prev_time,
|
||||
} => {
|
||||
let msg = format!(
|
||||
"Delta step resulted in invalid date\
|
||||
\nInitial start: {}\
|
||||
\nPrevious step: {}",
|
||||
Self::fmt_date_time(*start, *start_time),
|
||||
Self::fmt_date_time(*prev, *prev_time),
|
||||
);
|
||||
Self::print_at(sources, index, span, msg);
|
||||
let start_str = Self::fmt_date_time(*start, *start_time);
|
||||
let prev_str = Self::fmt_date_time(*prev, *prev_time);
|
||||
Diagnostic::error()
|
||||
.with_message("Delta step resulted in invalid date")
|
||||
.with_labels(vec![
|
||||
Label::primary(files.cs_id(*index), span).with_message("At this step")
|
||||
])
|
||||
.with_notes(vec![
|
||||
format!("Date before applying delta: {}", start_str),
|
||||
format!("Date before applying this step: {}", prev_str),
|
||||
])
|
||||
}
|
||||
Error::DeltaNoTime {
|
||||
index,
|
||||
span,
|
||||
start,
|
||||
prev,
|
||||
} => {
|
||||
let msg = format!(
|
||||
"Time-based delta step applied to date without time\
|
||||
\nInitial start: {}\
|
||||
\nPrevious step: {}",
|
||||
start, prev
|
||||
);
|
||||
Self::print_at(sources, index, span, msg);
|
||||
}
|
||||
} => Diagnostic::error()
|
||||
.with_message("Time-based delta step applied to date without time")
|
||||
.with_labels(vec![
|
||||
Label::primary(files.cs_id(*index), span).with_message("At this step")
|
||||
])
|
||||
.with_notes(vec![
|
||||
format!("Date before applying delta: {}", start),
|
||||
format!("Date before applying this step: {}", prev),
|
||||
]),
|
||||
Error::RepeatDidNotMoveForwards {
|
||||
index,
|
||||
span,
|
||||
from,
|
||||
to,
|
||||
} => {
|
||||
let msg = format!(
|
||||
"Repeat delta did not move forwards\
|
||||
\nMoved from {} to {}",
|
||||
from, to
|
||||
);
|
||||
Self::print_at(sources, index, span, msg);
|
||||
}
|
||||
} => Diagnostic::error()
|
||||
.with_message("Repeat delta did not move forwards")
|
||||
.with_labels(vec![
|
||||
Label::primary(files.cs_id(*index), span).with_message("This delta")
|
||||
])
|
||||
.with_notes(vec![format!("Moved from {} to {}", from, to)]),
|
||||
Error::RemindDidNotMoveBackwards {
|
||||
index,
|
||||
span,
|
||||
from,
|
||||
to,
|
||||
} => {
|
||||
let msg = format!(
|
||||
"Remind delta did not move backwards\
|
||||
\nMoved from {} to {}",
|
||||
from, to
|
||||
);
|
||||
Self::print_at(sources, index, span, msg);
|
||||
}
|
||||
Error::MoveWithoutSource { index, span } => {
|
||||
let msg = "Tried to move nonexisting entry".to_string();
|
||||
Self::print_at(sources, index, span, msg);
|
||||
}
|
||||
Error::TimedMoveWithoutTime { index, span } => {
|
||||
let msg = "Tried to move un-timed entry to new time".to_string();
|
||||
Self::print_at(sources, index, span, msg);
|
||||
}
|
||||
Error::DivByZero { index, span, date } => {
|
||||
let msg = format!(
|
||||
"Tried to divide by zero\
|
||||
\nAt date: {}",
|
||||
date
|
||||
);
|
||||
Self::print_at(sources, index, span, msg);
|
||||
}
|
||||
Error::ModByZero { index, span, date } => {
|
||||
let msg = format!(
|
||||
"Tried to modulo by zero\
|
||||
\nAt date: {}",
|
||||
date
|
||||
);
|
||||
Self::print_at(sources, index, span, msg);
|
||||
}
|
||||
} => Diagnostic::error()
|
||||
.with_message("Remind delta did not move backwards")
|
||||
.with_labels(vec![
|
||||
Label::primary(files.cs_id(*index), span).with_message("This delta")
|
||||
])
|
||||
.with_notes(vec![format!("Moved from {} to {}", from, to)]),
|
||||
Error::MoveWithoutSource { index, span } => Diagnostic::error()
|
||||
.with_message("Tried to move nonexistent entry")
|
||||
.with_labels(vec![
|
||||
Label::primary(files.cs_id(*index), span).with_message("Here")
|
||||
]),
|
||||
Error::TimedMoveWithoutTime { index, span } => Diagnostic::error()
|
||||
.with_message("Tried to move un-timed entry to new time")
|
||||
.with_labels(vec![
|
||||
Label::primary(files.cs_id(*index), span).with_message("Here")
|
||||
]),
|
||||
Error::DivByZero { index, span, date } => Diagnostic::error()
|
||||
.with_message("Tried to divide by zero")
|
||||
.with_labels(vec![
|
||||
Label::primary(files.cs_id(*index), span).with_message("This expression")
|
||||
])
|
||||
.with_notes(vec![format!("At date: {}", date)]),
|
||||
Error::ModByZero { index, span, date } => Diagnostic::error()
|
||||
.with_message("Tried to modulo by zero")
|
||||
.with_labels(vec![
|
||||
Label::primary(files.cs_id(*index), span).with_message("This expression")
|
||||
])
|
||||
.with_notes(vec![format!("At date: {}", date)]),
|
||||
Error::Easter {
|
||||
index,
|
||||
span,
|
||||
date,
|
||||
msg,
|
||||
} => {
|
||||
let msg = format!(
|
||||
"Failed to calculate easter\
|
||||
\nAt date: {}\
|
||||
\nReason: {}",
|
||||
date, msg
|
||||
);
|
||||
Self::print_at(sources, index, span, msg);
|
||||
}
|
||||
}
|
||||
} => Diagnostic::error()
|
||||
.with_message("Failed to calculate easter")
|
||||
.with_labels(vec![
|
||||
Label::primary(files.cs_id(*index), span).with_message("This expression")
|
||||
])
|
||||
.with_notes(vec![
|
||||
format!("At date: {}", date),
|
||||
format!("Reason: {}", msg),
|
||||
]),
|
||||
};
|
||||
files.eprint_diagnostic(&diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
#![warn(clippy::use_self)]
|
||||
|
||||
mod cli;
|
||||
// mod eval;
|
||||
mod eval;
|
||||
mod files;
|
||||
|
||||
fn main() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue