Move entries to a different time

This commit is contained in:
Joscha 2021-12-21 19:24:27 +01:00
parent 05a4582f13
commit 1ac39c69f2
9 changed files with 99 additions and 26 deletions

View file

@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Added ### Added
- `REMIND` statement - `REMIND` statement
- `MOVE` entries to a different time
## 0.1.0 - 2021-12-20 ## 0.1.0 - 2021-12-20

View file

@ -1,11 +1,11 @@
use std::collections::HashMap; use std::collections::HashMap;
use chrono::NaiveDate; use chrono::{Duration, NaiveDate};
use crate::files::commands::{ use crate::files::commands::{
self, BirthdaySpec, Command, Done, DoneDate, Note, Spec, Statement, Task, self, BirthdaySpec, Command, Done, DoneDate, Note, Spec, Statement, Task,
}; };
use crate::files::primitives::{Span, Spanned}; use crate::files::primitives::{Span, Spanned, Time};
use crate::files::SourcedCommand; use crate::files::SourcedCommand;
use super::date::Dates; use super::date::Dates;
@ -220,7 +220,12 @@ impl<'a> CommandState<'a> {
Statement::From(date) => self.from = *date, Statement::From(date) => self.from = *date,
Statement::Until(date) => self.until = *date, Statement::Until(date) => self.until = *date,
Statement::Except(date) => self.eval_except(*date), Statement::Except(date) => self.eval_except(*date),
Statement::Move { span, from, to } => self.eval_move(*span, *from, *to)?, Statement::Move {
span,
from,
to,
to_time,
} => self.eval_move(*span, *from, *to, *to_time)?,
Statement::Remind(delta) => self.eval_remind(delta), Statement::Remind(delta) => self.eval_remind(delta),
} }
Ok(()) Ok(())
@ -242,13 +247,31 @@ impl<'a> CommandState<'a> {
self.dated.remove(&date); self.dated.remove(&date);
} }
fn eval_move(&mut self, span: Span, from: NaiveDate, to: NaiveDate) -> Result<()> { fn eval_move(
&mut self,
span: Span,
from: NaiveDate,
to: Option<NaiveDate>,
to_time: Option<Time>,
) -> Result<()> {
if let Some(mut entry) = self.dated.remove(&from) { if let Some(mut entry) = self.dated.remove(&from) {
if let Some(dates) = entry.dates { let mut dates = entry.dates.expect("comes from self.dated");
let delta = to - from;
entry.dates = Some(dates.move_by(delta)); // Determine delta
let mut delta = Duration::zero();
if let Some(to) = to {
delta = delta + (to - dates.root());
} }
self.dated.insert(to, entry); if let Some(to_time) = to_time {
if let Some((root, _)) = dates.times() {
delta = delta + Duration::minutes(root.minutes_to(to_time));
}
}
dates = dates.move_by(delta);
entry.dates = Some(dates);
self.dated.insert(dates.root(), entry);
Ok(()) Ok(())
} else { } else {
Err(Error::MoveWithoutSource { Err(Error::MoveWithoutSource {

View file

@ -103,11 +103,24 @@ impl Dates {
} }
pub fn move_by(&self, delta: Duration) -> Self { pub fn move_by(&self, delta: Duration) -> Self {
Self { let mut result = *self;
root: self.root + delta,
other: self.other + delta, // Modify dates
times: self.times, result.root += delta;
result.other += delta;
// Modify times if necessary (may further modify dates)
const MINUTES_PER_DAY: i64 = 24 * 60;
let minutes = delta.num_minutes() % MINUTES_PER_DAY; // May be negative
if let Some(times) = self.times {
let (root_days, root) = times.root.add_minutes(minutes);
let (other_days, other) = times.other.add_minutes(minutes);
result.root += Duration::days(root_days);
result.other += Duration::days(other_days);
result.times = Some(Times { root, other });
} }
result
} }
} }

View file

@ -258,8 +258,8 @@ impl DeltaEval {
None => return Err(self.err_time(span)), None => return Err(self.err_time(span)),
}; };
let (days, time) = time.add_hours(amount); let (days, time) = time.add_hours(amount.into());
self.curr += Duration::days(days.into()); self.curr += Duration::days(days);
self.curr_time = Some(time); self.curr_time = Some(time);
Ok(()) Ok(())
} }
@ -270,8 +270,8 @@ impl DeltaEval {
None => return Err(self.err_time(span)), None => return Err(self.err_time(span)),
}; };
let (days, time) = time.add_minutes(amount); let (days, time) = time.add_minutes(amount.into());
self.curr += Duration::days(days.into()); self.curr += Duration::days(days);
self.curr_time = Some(time); self.curr_time = Some(time);
Ok(()) Ok(())
} }

View file

@ -265,7 +265,8 @@ pub enum Statement {
Move { Move {
span: Span, span: Span,
from: NaiveDate, from: NaiveDate,
to: NaiveDate, to: Option<NaiveDate>,
to_time: Option<Time>,
}, },
Remind(Option<Spanned<Delta>>), Remind(Option<Spanned<Delta>>),
} }

View file

@ -220,7 +220,14 @@ 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 {
from, to, to_time, ..
} => match (to, to_time) {
(None, None) => unreachable!(),
(Some(to), None) => writeln!(f, "MOVE {} TO {}", from, to),
(None, Some(to_time)) => writeln!(f, "MOVE {} TO {}", from, to_time),
(Some(to), Some(to_time)) => writeln!(f, "MOVE {} TO {} {}", from, to, to_time),
},
Statement::Remind(Some(delta)) => writeln!(f, "REMIND {}", delta), Statement::Remind(Some(delta)) => writeln!(f, "REMIND {}", delta),
Statement::Remind(None) => writeln!(f, "REMIND *"), Statement::Remind(None) => writeln!(f, "REMIND *"),
} }

View file

@ -106,7 +106,7 @@ stmt_bdate = !{ "BDATE" ~ bdatum ~ eol }
stmt_from = !{ "FROM" ~ (datum | "*") ~ eol } stmt_from = !{ "FROM" ~ (datum | "*") ~ eol }
stmt_until = !{ "UNTIL" ~ (datum | "*") ~ eol } stmt_until = !{ "UNTIL" ~ (datum | "*") ~ eol }
stmt_except = !{ "EXCEPT" ~ datum ~ eol } stmt_except = !{ "EXCEPT" ~ datum ~ eol }
stmt_move = !{ "MOVE" ~ datum ~ "TO" ~ datum ~ eol } stmt_move = !{ "MOVE" ~ datum ~ "TO" ~ (datum ~ time? | time) ~ eol }
stmt_remind = !{ "REMIND" ~ (delta | "*") ~ eol } stmt_remind = !{ "REMIND" ~ (delta | "*") ~ eol }
statements = { (stmt_date | stmt_bdate | stmt_from | stmt_until | stmt_except | stmt_move | stmt_remind)* } statements = { (stmt_date | stmt_bdate | stmt_from | stmt_until | stmt_except | stmt_move | stmt_remind)* }

View file

@ -618,9 +618,23 @@ fn parse_stmt_move(p: Pair<'_, Rule>) -> Result<Statement> {
let span = (&p.as_span()).into(); 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;
assert_eq!(p.next(), None); let mut to = None;
Ok(Statement::Move { span, from, to }) let mut to_time = None;
for p in p {
match p.as_rule() {
Rule::datum => to = Some(parse_datum(p)?.value),
Rule::time => to_time = Some(parse_time(p)?.value),
_ => unreachable!(),
}
}
Ok(Statement::Move {
span,
from,
to,
to_time,
})
} }
fn parse_stmt_remind(p: Pair<'_, Rule>) -> Result<Statement> { fn parse_stmt_remind(p: Pair<'_, Rule>) -> Result<Statement> {

View file

@ -98,10 +98,15 @@ impl Time {
true true
} }
pub fn add_minutes(&self, amount: i32) -> (i32, Self) { /// How many minutes into the day this time is.
fn minutes(&self) -> i64 {
(self.hour as i64) * 60 + (self.min as i64)
}
pub fn add_minutes(&self, amount: i64) -> (i64, Self) {
match amount.cmp(&0) { match amount.cmp(&0) {
Ordering::Less => { Ordering::Less => {
let mut mins = (self.hour as i32) * 60 + (self.min as i32) + amount; let mut mins = self.minutes() + amount;
let days = mins.div_euclid(60 * 24); let days = mins.div_euclid(60 * 24);
mins = mins.rem_euclid(60 * 24); mins = mins.rem_euclid(60 * 24);
@ -111,7 +116,7 @@ impl Time {
(days, Self::new(hour, min)) (days, Self::new(hour, min))
} }
Ordering::Greater => { Ordering::Greater => {
let mut mins = (self.hour as i32) * 60 + (self.min as i32) + amount; let mut mins = self.minutes() + amount;
let mut days = mins.div_euclid(60 * 24); let mut days = mins.div_euclid(60 * 24);
mins = mins.rem_euclid(60 * 24); mins = mins.rem_euclid(60 * 24);
@ -130,9 +135,18 @@ impl Time {
} }
} }
pub fn add_hours(&self, amount: i32) -> (i32, Self) { pub fn add_hours(&self, amount: i64) -> (i64, Self) {
self.add_minutes(amount * 60) self.add_minutes(amount * 60)
} }
/// `a.minutes_to(b)` returns the minutes from `a` to `b`, meaning it is
/// greater than 0 if `a` is earlier than `b`.
///
/// May return weird amounts if [`Self::in_normal_range`] is not true for
/// both.
pub fn minutes_to(&self, other: Self) -> i64 {
other.minutes() - self.minutes()
}
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]