commit - c25ff820f4b12628b9df9a9b35435c01da6f1606
commit + bc6fb9a91750c4bf7c6dc6318f8a6c6bf0e0289f
blob - f56b69375d1a7f1da89b96ad36c235df0a6dff6f
blob + a72e1d4282795ab9a62b1b4a888d4ad1c2ec93ab
--- src/types.rs
+++ src/types.rs
/// Advance the due date of the [`Task`] by the value of the [`Recurring`] [`Tag`].
///
/// The due date is only advanced if there is one.
+ /// Depending on whether the [`Recurring`] rule is strict or not, the next due date will be
+ /// based on the current date or the date given in the due [`Tag`], respectively.
+ ///
+ /// The current date is calculated internally. If a different current date is to be given, use
+ /// [`next_due_with_rec_and_date`] instead.
#[must_use]
pub fn next_due_with_rec(&self, rec: &Recurring) -> Option<Task> {
- // TODO: set new creation date, too
- // TODO: handle strict recurring
- // non-strict: next due date is x amount after due date
- // strict: next due date is x amount after *current* date
+ let current_date = chrono::offset::Utc::now().date_naive();
+
+ self.next_due_with_rec_and_date(rec, current_date)
+ }
+
+ /// Advance the due date of the [`Task`] by the value of the [`Recurring`] [`Tag`].
+ ///
+ /// The due date is only advanced if both a due date *and* a [`Recurring`] [`Tag`] are present.
+ /// Depending on whether the [`Recurring`] rule is strict or not, the next due date will be
+ /// based on the current date or the date given in the due [`Tag`], respectively.
+ ///
+ /// The current date is calculated internally. If a different current date is to be given, use
+ /// [`next_due_with_rec_and_date`] instead.
+ #[must_use]
+ pub fn next_due(&self) -> Option<Task> {
+ self.next_due_with_rec(&self.rec()?)
+ }
+
+ /// Advance the due date of the [`Task`] by the value of the [`Recurring`] [`Tag`], given an
+ /// explicit current date instead of computing one internally.
+ ///
+ /// The due date is only advanced if both a due date *and* a [`Recurring`] [`Tag`] are present.
+ /// Depending on whether the [`Recurring`] rule is strict or not, the next due date will be
+ /// based on the current date or the date given in the due [`Tag`], respectively.
+ // mostly an internal function of `next_due` and `next_due_with_rec` for easier testing that is
+ // not relying on an implicit "today" date. only public for the chance it *might* be useful.
+ #[must_use]
+ pub fn next_due_with_rec_and_date(
+ &self,
+ rec: &Recurring,
+ current_date: NaiveDate,
+ ) -> Option<Task> {
+ // abort early if no previous due date was set
+ // XXX: if performance is needed *badly*, optimise to "lazily" compute the due date when it
+ // is needed
let prev_due = self.due()?;
+
+ let reference_date = if rec.strict { current_date } else { prev_due };
+
let due = match rec.amount.unit {
- TimeUnit::Day => prev_due + Days::new(rec.amount.amount.try_into().ok()?),
- TimeUnit::Week => prev_due + Days::new((rec.amount.amount * 7).try_into().ok()?),
- TimeUnit::Month => prev_due + Months::new(rec.amount.amount.try_into().ok()?),
- TimeUnit::Year => prev_due + Months::new((rec.amount.amount * 12).try_into().ok()?),
+ TimeUnit::Day => reference_date + Days::new(rec.amount.amount.try_into().ok()?),
+ TimeUnit::Week => reference_date + Days::new((rec.amount.amount * 7).try_into().ok()?),
+ TimeUnit::Month => reference_date + Months::new(rec.amount.amount.try_into().ok()?),
+ TimeUnit::Year => {
+ reference_date + Months::new((rec.amount.amount * 12).try_into().ok()?)
+ }
};
Some(self.set_due(due))
}
-
- /// Advance the due date of the [`Task`] by the value of the [`Recurring`] [`Tag`].
- ///
- /// The due date is only advanced if both a due date *and* a [`Recurring`] [`Tag`] are present.
- #[must_use]
- pub fn next_due(&self) -> Option<Task> {
- self.next_due_with_rec(&self.rec()?)
- }
}
impl<'a>
fn test_set_due() {
let task = Task::try_from("test with due date due:2023-01-08").unwrap();
let due = NaiveDate::from_ymd_opt(2023, 1, 9).unwrap();
- let new_task = task.set_due(due.clone());
+ let new_task = task.set_due(due);
assert_eq!(
task.tags(),
vec![Tag::Due(NaiveDate::from_ymd_opt(2023, 1, 8).unwrap())]
}
#[test]
- fn test_next_due_with_rec() {
- let task_without_due = Task::try_from("2023-01-08 boring task").unwrap();
- let rec = Recurring {
+ fn test_next_due_with_rec_and_date() {
+ let current_date = NaiveDate::from_ymd_opt(2023, 1, 8).unwrap();
+ let rec_not_strict = Recurring {
strict: false,
amount: TimeAmount {
amount: 4,
unit: TimeUnit::Day,
},
};
- assert_eq!(task_without_due.next_due_with_rec(&rec), None);
+ let rec_strict = Recurring {
+ strict: true,
+ amount: TimeAmount {
+ amount: 4,
+ unit: TimeUnit::Day,
+ },
+ };
- let task_with_due = Task::try_from("2023-01-08 due task due:2023-01-09").unwrap();
- let new_task = task_with_due.next_due_with_rec(&rec).unwrap();
+ // no due date given -> no new task
+ let task_without_due = Task::try_from("2023-01-06 boring task").unwrap();
+ assert_eq!(
+ task_without_due.next_due_with_rec_and_date(&rec_not_strict, current_date),
+ None
+ );
+ // not strict -> due + rec, current date is irrelevant
+ let task_with_due = Task::try_from("2023-01-06 due task due:2023-01-09").unwrap();
+ let new_task = task_with_due
+ .next_due_with_rec_and_date(&rec_not_strict, current_date)
+ .unwrap();
+
assert_eq!(
new_task.due().unwrap(),
- NaiveDate::from_ymd_opt(2023, 01, 13).unwrap()
+ NaiveDate::from_ymd_opt(2023, 1, 13).unwrap()
);
- }
- #[test]
- fn test_next_due() {
- let task_without_due_without_rec = Task::try_from("2023-01-08 boring task").unwrap();
- let task_without_due_with_rec = Task::try_from("2023-01-08 boring task rec:+4d").unwrap();
- let task_with_due_without_rec =
- Task::try_from("2023-01-08 boring task due:2023-01-09").unwrap();
- let task_with_due_with_rec =
- Task::try_from("2023-01-08 boring task due:2023-01-09 rec:+4d").unwrap();
+ // strict -> current_date + rec, due is irrelevant
+ let new_strict_task = task_with_due
+ .next_due_with_rec_and_date(&rec_strict, current_date)
+ .unwrap();
- assert_eq!(task_without_due_without_rec.next_due(), None);
- assert_eq!(task_without_due_with_rec.next_due(), None);
- assert_eq!(task_with_due_without_rec.next_due(), None);
assert_eq!(
- task_with_due_with_rec.next_due().unwrap(),
- Task::try_from("2023-01-08 boring task due:2023-01-13 rec:+4d").unwrap()
+ new_strict_task.due().unwrap(),
+ NaiveDate::from_ymd_opt(2023, 1, 12).unwrap()
);
}
}