diff --git a/example.today b/example.today index b0f9d2f..9e85170 100644 --- a/example.today +++ b/example.today @@ -7,6 +7,11 @@ DATE sun 22:00 -- +2h DATE (wd = sun) 22:00 -- 24:00 DATE 2021-11-07 22:00 -- 24:00; +w DATE 2021-11-07 22:00 -- +2h; +w +DONE [2021-11-07] 2021-11-07 22:00 -- 24:00 +DONE [2021-11-14] 2021-11-14 22:00 -- 24:00 +DONE [2021-11-21] 2021-11-21 22:00 -- 24:00 +DONE [2021-11-28] 2021-11-28 22:00 -- 24:00 +DONE [2021-11-30] NOTE daily DATE * diff --git a/src/files/commands.rs b/src/files/commands.rs index 947e7e3..e132520 100644 --- a/src/files/commands.rs +++ b/src/files/commands.rs @@ -277,10 +277,31 @@ pub enum Spec { Formula(FormulaSpec), } +#[derive(Debug)] +pub enum DoneDate { + Date { + root: NaiveDate, + }, + DateWithTime { + root: NaiveDate, + root_time: Time, + }, + DateToDate { + root: NaiveDate, + other: NaiveDate, + }, + DateToDateWithTime { + root: NaiveDate, + root_time: Time, + other: NaiveDate, + other_time: Time, + }, +} + #[derive(Debug)] pub struct Done { - pub refering_to: Option, - pub created_at: Option<(NaiveDate, Time)>, + pub date: Option, + pub done_at: NaiveDate, } #[derive(Debug)] diff --git a/src/files/format.rs b/src/files/format.rs index 3cf5e01..2c8318a 100644 --- a/src/files/format.rs +++ b/src/files/format.rs @@ -3,8 +3,8 @@ use std::fmt; use chrono::Datelike; use super::commands::{ - Birthday, BirthdaySpec, Command, DateSpec, Delta, DeltaStep, Done, Expr, File, FormulaSpec, - Note, Spec, Task, Time, Var, Weekday, WeekdaySpec, + Birthday, BirthdaySpec, Command, DateSpec, Delta, DeltaStep, Done, DoneDate, Expr, File, + FormulaSpec, Note, Spec, Task, Time, Var, Weekday, WeekdaySpec, }; fn format_desc(f: &mut fmt::Formatter<'_>, desc: &[String]) -> fmt::Result { @@ -186,15 +186,28 @@ impl fmt::Display for Spec { } } +impl fmt::Display for DoneDate { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + DoneDate::Date { root } => write!(f, "{}", root), + DoneDate::DateWithTime { root, root_time } => write!(f, "{} {}", root, root_time), + DoneDate::DateToDate { root, other } => write!(f, "{} -- {}", root, other), + DoneDate::DateToDateWithTime { + root, + root_time, + other, + other_time, + } => write!(f, "{} {} -- {} {}", root, root_time, other, other_time), + } + } +} + impl fmt::Display for Done { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "DONE")?; - if let Some(date) = &self.refering_to { + write!(f, "DONE [{}]", self.done_at)?; + if let Some(date) = &self.date { write!(f, " {}", date)?; } - if let Some((date, time)) = &self.created_at { - write!(f, " ({} {})", date, time)?; - } writeln!(f) } } diff --git a/src/files/grammar.pest b/src/files/grammar.pest index f44a486..c9cb45e 100644 --- a/src/files/grammar.pest +++ b/src/files/grammar.pest @@ -107,8 +107,13 @@ from = !{ "FROM" ~ datum ~ eol } until = !{ "UNTIL" ~ datum ~ eol } except = !{ "EXCEPT" ~ datum ~ eol } -donedate = { "(" ~ datum ~ time ~ ")" } -done = !{ "DONE" ~ datum? ~ donedate? ~ eol } +donedate = { + datum ~ time ~ "--" ~ datum ~ time + | datum ~ time + | datum ~ "--" ~ datum + | datum +} +done = !{ "DONE" ~ "[" ~ datum ~ "]" ~ donedate? ~ eol } desc_line = { "#" ~ (" " ~ rest_any)? ~ eol } description = { desc_line* } diff --git a/src/files/parse.rs b/src/files/parse.rs index 27099fa..16c44e4 100644 --- a/src/files/parse.rs +++ b/src/files/parse.rs @@ -8,8 +8,8 @@ use pest::prec_climber::{Assoc, Operator, PrecClimber}; use pest::{Parser, Span}; use super::commands::{ - Birthday, BirthdaySpec, Command, DateSpec, Delta, DeltaStep, Done, Expr, File, FormulaSpec, - Note, Spec, Task, Time, Var, Weekday, WeekdaySpec, + Birthday, BirthdaySpec, Command, DateSpec, Delta, DeltaStep, Done, DoneDate, Expr, File, + FormulaSpec, Note, Spec, Task, Time, Var, Weekday, WeekdaySpec, }; #[derive(pest_derive::Parser)] @@ -538,36 +538,52 @@ fn parse_except(p: Pair<'_, Rule>) -> Result { parse_datum(p.into_inner().next().unwrap()) } -fn parse_donedate(p: Pair<'_, Rule>) -> Result<(NaiveDate, Time)> { +fn parse_donedate(p: Pair<'_, Rule>) -> Result { assert_eq!(p.as_rule(), Rule::donedate); - let mut p = p.into_inner(); + let mut ps = p.into_inner().collect::>(); - let date = parse_datum(p.next().unwrap())?; - let time = parse_time(p.next().unwrap())?; - - assert_eq!(p.next(), None); - - Ok((date, time)) + // Popping the elements off of the vector in reverse so I don't have to + // shuffle them around weirdly. In Haskell, I would've just pattern-matched + // the list ;-; + Ok(match ps.len() { + 1 => DoneDate::Date { + root: parse_datum(ps.pop().unwrap())?, + }, + 2 => match ps[1].as_rule() { + Rule::time => DoneDate::DateWithTime { + root_time: parse_time(ps.pop().unwrap())?, + root: parse_datum(ps.pop().unwrap())?, + }, + Rule::datum => DoneDate::DateToDate { + other: parse_datum(ps.pop().unwrap())?, + root: parse_datum(ps.pop().unwrap())?, + }, + _ => unreachable!(), + }, + 4 => DoneDate::DateToDateWithTime { + other_time: parse_time(ps.pop().unwrap())?, + other: parse_datum(ps.pop().unwrap())?, + root_time: parse_time(ps.pop().unwrap())?, + root: parse_datum(ps.pop().unwrap())?, + }, + _ => unreachable!(), + }) } fn parse_done(p: Pair<'_, Rule>) -> Result { assert_eq!(p.as_rule(), Rule::done); + let mut p = p.into_inner(); - let mut refering_to = None; - let mut created_at = None; + let done_at = parse_datum(p.next().unwrap())?; + let date = if let Some(p) = p.next() { + Some(parse_donedate(p)?) + } else { + None + }; - for ele in p.into_inner() { - match ele.as_rule() { - Rule::datum => refering_to = Some(parse_datum(ele)?), - Rule::donedate => created_at = Some(parse_donedate(ele)?), - _ => unreachable!(), - } - } + assert_eq!(p.next(), None); - Ok(Done { - refering_to, - created_at, - }) + Ok(Done { date, done_at }) } #[derive(Default)]