Move entries to a different time
This commit is contained in:
parent
05a4582f13
commit
1ac39c69f2
9 changed files with 99 additions and 26 deletions
|
|
@ -1,11 +1,11 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use chrono::NaiveDate;
|
||||
use chrono::{Duration, NaiveDate};
|
||||
|
||||
use crate::files::commands::{
|
||||
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 super::date::Dates;
|
||||
|
|
@ -220,7 +220,12 @@ impl<'a> CommandState<'a> {
|
|||
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)?,
|
||||
Statement::Move {
|
||||
span,
|
||||
from,
|
||||
to,
|
||||
to_time,
|
||||
} => self.eval_move(*span, *from, *to, *to_time)?,
|
||||
Statement::Remind(delta) => self.eval_remind(delta),
|
||||
}
|
||||
Ok(())
|
||||
|
|
@ -242,13 +247,31 @@ impl<'a> CommandState<'a> {
|
|||
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(dates) = entry.dates {
|
||||
let delta = to - from;
|
||||
entry.dates = Some(dates.move_by(delta));
|
||||
let mut dates = entry.dates.expect("comes from self.dated");
|
||||
|
||||
// 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(())
|
||||
} else {
|
||||
Err(Error::MoveWithoutSource {
|
||||
|
|
|
|||
|
|
@ -103,11 +103,24 @@ impl Dates {
|
|||
}
|
||||
|
||||
pub fn move_by(&self, delta: Duration) -> Self {
|
||||
Self {
|
||||
root: self.root + delta,
|
||||
other: self.other + delta,
|
||||
times: self.times,
|
||||
let mut result = *self;
|
||||
|
||||
// Modify dates
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -258,8 +258,8 @@ impl DeltaEval {
|
|||
None => return Err(self.err_time(span)),
|
||||
};
|
||||
|
||||
let (days, time) = time.add_hours(amount);
|
||||
self.curr += Duration::days(days.into());
|
||||
let (days, time) = time.add_hours(amount.into());
|
||||
self.curr += Duration::days(days);
|
||||
self.curr_time = Some(time);
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -270,8 +270,8 @@ impl DeltaEval {
|
|||
None => return Err(self.err_time(span)),
|
||||
};
|
||||
|
||||
let (days, time) = time.add_minutes(amount);
|
||||
self.curr += Duration::days(days.into());
|
||||
let (days, time) = time.add_minutes(amount.into());
|
||||
self.curr += Duration::days(days);
|
||||
self.curr_time = Some(time);
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -265,7 +265,8 @@ pub enum Statement {
|
|||
Move {
|
||||
span: Span,
|
||||
from: NaiveDate,
|
||||
to: NaiveDate,
|
||||
to: Option<NaiveDate>,
|
||||
to_time: Option<Time>,
|
||||
},
|
||||
Remind(Option<Spanned<Delta>>),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -220,7 +220,14 @@ impl fmt::Display for Statement {
|
|||
Statement::Until(Some(date)) => writeln!(f, "UNTIL {}", date),
|
||||
Statement::Until(None) => writeln!(f, "UNTIL *"),
|
||||
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(None) => writeln!(f, "REMIND *"),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ stmt_bdate = !{ "BDATE" ~ bdatum ~ eol }
|
|||
stmt_from = !{ "FROM" ~ (datum | "*") ~ eol }
|
||||
stmt_until = !{ "UNTIL" ~ (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 }
|
||||
|
||||
statements = { (stmt_date | stmt_bdate | stmt_from | stmt_until | stmt_except | stmt_move | stmt_remind)* }
|
||||
|
|
|
|||
|
|
@ -618,9 +618,23 @@ fn parse_stmt_move(p: Pair<'_, Rule>) -> Result<Statement> {
|
|||
let span = (&p.as_span()).into();
|
||||
let mut p = p.into_inner();
|
||||
let from = parse_datum(p.next().unwrap())?.value;
|
||||
let to = parse_datum(p.next().unwrap())?.value;
|
||||
assert_eq!(p.next(), None);
|
||||
Ok(Statement::Move { span, from, to })
|
||||
|
||||
let mut to = None;
|
||||
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> {
|
||||
|
|
|
|||
|
|
@ -98,10 +98,15 @@ impl Time {
|
|||
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) {
|
||||
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);
|
||||
mins = mins.rem_euclid(60 * 24);
|
||||
|
|
@ -111,7 +116,7 @@ impl Time {
|
|||
(days, Self::new(hour, min))
|
||||
}
|
||||
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);
|
||||
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)
|
||||
}
|
||||
|
||||
/// `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)]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue