Evaluate some simple statements
This commit is contained in:
parent
778576a63c
commit
3a219ecac2
6 changed files with 184 additions and 12 deletions
|
|
@ -2,9 +2,10 @@ use std::collections::HashMap;
|
||||||
|
|
||||||
use chrono::NaiveDate;
|
use chrono::NaiveDate;
|
||||||
|
|
||||||
use crate::files::SourcedCommand;
|
use crate::files::commands::{BirthdaySpec, Command, Done, Note, Span, Spec, Statement, Task};
|
||||||
|
use crate::files::{Source, SourcedCommand};
|
||||||
|
|
||||||
use super::{DateRange, Entry, Result};
|
use super::{DateRange, Entry, EntryKind, Error, Result};
|
||||||
|
|
||||||
pub struct CommandState<'a> {
|
pub struct CommandState<'a> {
|
||||||
command: SourcedCommand<'a>,
|
command: SourcedCommand<'a>,
|
||||||
|
|
@ -12,7 +13,9 @@ pub struct CommandState<'a> {
|
||||||
|
|
||||||
from: Option<NaiveDate>,
|
from: Option<NaiveDate>,
|
||||||
until: Option<NaiveDate>,
|
until: Option<NaiveDate>,
|
||||||
entries: HashMap<NaiveDate, Entry>,
|
|
||||||
|
dated: HashMap<NaiveDate, Entry>,
|
||||||
|
undated: Vec<Entry>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CommandState<'a> {
|
impl<'a> CommandState<'a> {
|
||||||
|
|
@ -22,15 +25,130 @@ impl<'a> CommandState<'a> {
|
||||||
command,
|
command,
|
||||||
from: None,
|
from: None,
|
||||||
until: None,
|
until: None,
|
||||||
entries: HashMap::new(),
|
dated: HashMap::new(),
|
||||||
|
undated: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval(self) -> Result<Self> {
|
pub fn eval(mut self) -> Result<Self> {
|
||||||
todo!()
|
match self.command.command {
|
||||||
|
Command::Task(task) => self.eval_task(task)?,
|
||||||
|
Command::Note(note) => self.eval_note(note)?,
|
||||||
|
}
|
||||||
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn entries(self) -> Vec<Entry> {
|
pub fn entries(self) -> Vec<Entry> {
|
||||||
self.entries.into_values().collect()
|
self.dated
|
||||||
|
.into_values()
|
||||||
|
.chain(self.undated.into_iter())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper functions
|
||||||
|
|
||||||
|
fn title(&self) -> String {
|
||||||
|
self.command.command.title().to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn desc(&self) -> Vec<String> {
|
||||||
|
self.command.command.desc().to_vec()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn source(&self) -> Source {
|
||||||
|
self.command.source
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add an entry, respecting [`Self::from`] and [`Self::until`]. Does not
|
||||||
|
/// overwrite existing entries if a root date is specified.
|
||||||
|
fn add(&mut self, entry: Entry) {
|
||||||
|
if let Some(root) = entry.root {
|
||||||
|
if let Some(from) = self.from {
|
||||||
|
if root < from {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(until) = self.until {
|
||||||
|
if until < root {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.dated.entry(root).or_insert(entry);
|
||||||
|
} else {
|
||||||
|
self.undated.push(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add an entry, ignoring [`Self::from`] and [`Self::until`]. Always
|
||||||
|
/// overwrites existing entries if a root date is specified.
|
||||||
|
fn add_forced(&mut self, entry: Entry) {
|
||||||
|
if let Some(root) = entry.root {
|
||||||
|
self.dated.insert(root, entry);
|
||||||
|
} else {
|
||||||
|
self.undated.push(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actual evaluation
|
||||||
|
|
||||||
|
fn eval_task(&mut self, task: &Task) -> Result<()> {
|
||||||
|
for statement in &task.statements {
|
||||||
|
self.eval_statement(statement)?;
|
||||||
|
}
|
||||||
|
for done in &task.done {
|
||||||
|
self.eval_done(done);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_note(&mut self, note: &Note) -> Result<()> {
|
||||||
|
for statement in ¬e.statements {
|
||||||
|
self.eval_statement(statement)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_statement(&mut self, statement: &Statement) -> Result<()> {
|
||||||
|
match statement {
|
||||||
|
Statement::Date(spec) => self.eval_date(spec)?,
|
||||||
|
Statement::BDate(spec) => self.eval_bdate(spec)?,
|
||||||
|
Statement::From(date) => self.from = *date,
|
||||||
|
Statement::Until(date) => self.until = *date,
|
||||||
|
Statement::Except(date) => self.eval_except(*date),
|
||||||
|
Statement::Move { span, from, to } => self.eval_move(*span, *from, *to)?,
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_date(&mut self, spec: &Spec) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_bdate(&mut self, spec: &BirthdaySpec) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_except(&mut self, date: NaiveDate) {
|
||||||
|
self.dated.remove(&date);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_move(&mut self, span: Span, from: NaiveDate, to: NaiveDate) -> Result<()> {
|
||||||
|
if let Some(entry) = self.dated.remove(&from) {
|
||||||
|
self.dated.insert(to, entry);
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Error::MoveWithoutSource { span })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_done(&mut self, done: &Done) {
|
||||||
|
self.add_forced(Entry {
|
||||||
|
kind: EntryKind::TaskDone(done.done_at),
|
||||||
|
title: self.title(),
|
||||||
|
desc: self.desc(),
|
||||||
|
source: self.source(),
|
||||||
|
dates: done.date.map(|date| date.into()),
|
||||||
|
root: done.date.map(|date| date.root()),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use chrono::NaiveDate;
|
use chrono::NaiveDate;
|
||||||
|
|
||||||
use crate::files::commands::Time;
|
use crate::files::commands::{DoneDate, Time};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct Times {
|
pub struct Times {
|
||||||
|
|
@ -37,8 +37,8 @@ impl Dates {
|
||||||
|
|
||||||
pub fn new_with_time(
|
pub fn new_with_time(
|
||||||
start: NaiveDate,
|
start: NaiveDate,
|
||||||
end: NaiveDate,
|
|
||||||
start_time: Time,
|
start_time: Time,
|
||||||
|
end: NaiveDate,
|
||||||
end_time: Time,
|
end_time: Time,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
assert!(start <= end);
|
assert!(start <= end);
|
||||||
|
|
@ -75,3 +75,33 @@ impl Dates {
|
||||||
self.times.as_ref().map(Times::end)
|
self.times.as_ref().map(Times::end)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<DoneDate> for Dates {
|
||||||
|
fn from(date: DoneDate) -> Self {
|
||||||
|
match date {
|
||||||
|
DoneDate::Date { root } => Self::new(root, root),
|
||||||
|
DoneDate::DateWithTime { root, root_time } => {
|
||||||
|
Self::new_with_time(root, root_time, root, root_time)
|
||||||
|
}
|
||||||
|
DoneDate::DateToDate { root, other } => {
|
||||||
|
if root <= other {
|
||||||
|
Self::new(root, other)
|
||||||
|
} else {
|
||||||
|
Self::new(other, root)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DoneDate::DateToDateWithTime {
|
||||||
|
root,
|
||||||
|
root_time,
|
||||||
|
other,
|
||||||
|
other_time,
|
||||||
|
} => {
|
||||||
|
if root < other || (root == other && root_time <= other_time) {
|
||||||
|
Self::new_with_time(root, root_time, other, other_time)
|
||||||
|
} else {
|
||||||
|
Self::new_with_time(other, other_time, root, root_time)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,9 @@ pub enum Error {
|
||||||
start: NaiveDate,
|
start: NaiveDate,
|
||||||
prev: NaiveDate,
|
prev: NaiveDate,
|
||||||
},
|
},
|
||||||
|
/// A `MOVE a TO b` statement was executed, but there was no entry at the
|
||||||
|
/// date `a`.
|
||||||
|
MoveWithoutSource { span: Span },
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = result::Result<T, Error>;
|
pub type Result<T> = result::Result<T, Error>;
|
||||||
|
|
|
||||||
|
|
@ -355,7 +355,11 @@ pub enum Statement {
|
||||||
From(Option<NaiveDate>),
|
From(Option<NaiveDate>),
|
||||||
Until(Option<NaiveDate>),
|
Until(Option<NaiveDate>),
|
||||||
Except(NaiveDate), // TODO Allow excluding ranges
|
Except(NaiveDate), // TODO Allow excluding ranges
|
||||||
Move(NaiveDate, NaiveDate),
|
Move {
|
||||||
|
span: Span,
|
||||||
|
from: NaiveDate,
|
||||||
|
to: NaiveDate,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
|
@ -440,6 +444,22 @@ pub enum Command {
|
||||||
Note(Note),
|
Note(Note),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Command {
|
||||||
|
pub fn title(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Command::Task(task) => &task.title,
|
||||||
|
Command::Note(note) => ¬e.title,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn desc(&self) -> &[String] {
|
||||||
|
match self {
|
||||||
|
Command::Task(task) => &task.desc,
|
||||||
|
Command::Note(note) => ¬e.desc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct File {
|
pub struct File {
|
||||||
pub contents: String,
|
pub contents: String,
|
||||||
|
|
|
||||||
|
|
@ -219,7 +219,7 @@ impl fmt::Display for Statement {
|
||||||
Statement::Until(Some(date)) => writeln!(f, "UNTIL {}", date),
|
Statement::Until(Some(date)) => writeln!(f, "UNTIL {}", date),
|
||||||
Statement::Until(None) => writeln!(f, "UNTIL *"),
|
Statement::Until(None) => writeln!(f, "UNTIL *"),
|
||||||
Statement::Except(date) => writeln!(f, "EXCEPT {}", date),
|
Statement::Except(date) => writeln!(f, "EXCEPT {}", date),
|
||||||
Statement::Move(from, to) => writeln!(f, "MOVE {} TO {}", from, to),
|
Statement::Move { span, from, to } => writeln!(f, "MOVE {} TO {}", from, to),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -605,11 +605,12 @@ fn parse_stmt_except(p: Pair<'_, Rule>) -> Result<Statement> {
|
||||||
|
|
||||||
fn parse_stmt_move(p: Pair<'_, Rule>) -> Result<Statement> {
|
fn parse_stmt_move(p: Pair<'_, Rule>) -> Result<Statement> {
|
||||||
assert_eq!(p.as_rule(), Rule::stmt_move);
|
assert_eq!(p.as_rule(), Rule::stmt_move);
|
||||||
|
let span = (&p.as_span()).into();
|
||||||
let mut p = p.into_inner();
|
let mut p = p.into_inner();
|
||||||
let from = parse_datum(p.next().unwrap())?.value;
|
let from = parse_datum(p.next().unwrap())?.value;
|
||||||
let to = parse_datum(p.next().unwrap())?.value;
|
let to = parse_datum(p.next().unwrap())?.value;
|
||||||
assert_eq!(p.next(), None);
|
assert_eq!(p.next(), None);
|
||||||
Ok(Statement::Move(from, to))
|
Ok(Statement::Move { span, from, to })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_statements(p: Pair<'_, Rule>) -> Result<Vec<Statement>> {
|
fn parse_statements(p: Pair<'_, Rule>) -> Result<Vec<Statement>> {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue