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:
Joscha 2022-01-02 15:05:37 +01:00
parent 810ec67cf7
commit 8ae691bc3c
8 changed files with 238 additions and 215 deletions

View file

@ -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,

View file

@ -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) => &note.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(&note.statements) {
for statement in &note.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),

View file

@ -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(()),

View file

@ -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(())
}

View file

@ -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);
}
}

View file

@ -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),
)

View file

@ -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>;

View file

@ -4,7 +4,7 @@
#![warn(clippy::use_self)]
mod cli;
// mod eval;
mod eval;
mod files;
fn main() {