diff --git a/cli/src/invocation/report.rs b/cli/src/invocation/report.rs index cf4008562..d67dae4fb 100644 --- a/cli/src/invocation/report.rs +++ b/cli/src/invocation/report.rs @@ -403,8 +403,11 @@ mod test { }; let task = replica.get_task(uuids[0]).unwrap().unwrap(); - assert_eq!(task_column(&task, &column, &working_set), s!("+bar +foo")); + assert_eq!( + task_column(&task, &column, &working_set), + s!("+PENDING +bar +foo") + ); let task = replica.get_task(uuids[2]).unwrap().unwrap(); - assert_eq!(task_column(&task, &column, &working_set), s!("")); + assert_eq!(task_column(&task, &column, &working_set), s!("+PENDING")); } } diff --git a/docs/src/tags.md b/docs/src/tags.md index ea070d987..cf9d80102 100644 --- a/docs/src/tags.md +++ b/docs/src/tags.md @@ -21,3 +21,6 @@ The following synthetic tags are defined: * `WAITING` - set if the task is waiting (has a `wait` property with a date in the future) * `ACTIVE` - set if the task is active (has been started and not stopped) +* `PENDING` - set if the task is pending (not completed or deleted) +* `COMPLETED` - set if the task has been completed +* `DELETED` - set if the task has been deleted (but not yet flushed from the task list) diff --git a/taskchampion/src/task/tag.rs b/taskchampion/src/task/tag.rs index d8689e6af..7554a57dd 100644 --- a/taskchampion/src/task/tag.rs +++ b/taskchampion/src/task/tag.rs @@ -126,8 +126,13 @@ impl AsRef for Tag { )] #[strum(serialize_all = "SCREAMING_SNAKE_CASE")] pub(super) enum SyntheticTag { + // When adding items here, also implement and test them in `task.rs` and document them in + // `docs/src/tags.md`. Waiting, Active, + Pending, + Completed, + Deleted, } #[cfg(test)] diff --git a/taskchampion/src/task/task.rs b/taskchampion/src/task/task.rs index 9695247cf..40b2d6b0f 100644 --- a/taskchampion/src/task/task.rs +++ b/taskchampion/src/task/task.rs @@ -96,6 +96,9 @@ impl Task { match synth { SyntheticTag::Waiting => self.is_waiting(), SyntheticTag::Active => self.is_active(), + SyntheticTag::Pending => self.get_status() == Status::Pending, + SyntheticTag::Completed => self.get_status() == Status::Completed, + SyntheticTag::Deleted => self.get_status() == Status::Deleted, } } @@ -202,6 +205,11 @@ impl<'r> TaskMut<'r> { Ok(()) } + /// Mark this task as complete + pub fn done(&mut self) -> anyhow::Result<()> { + self.set_status(Status::Completed) + } + /// Add a tag to this task. Does nothing if the tag is already present. pub fn add_tag(&mut self, tag: &Tag) -> anyhow::Result<()> { if tag.is_synthetic() { @@ -392,6 +400,7 @@ mod test { assert!(task.has_tag(&utag("abc"))); assert!(!task.has_tag(&utag("def"))); assert!(task.has_tag(&stag(SyntheticTag::Active))); + assert!(task.has_tag(&stag(SyntheticTag::Pending))); assert!(!task.has_tag(&stag(SyntheticTag::Waiting))); } @@ -411,10 +420,14 @@ mod test { let mut tags: Vec<_> = task.get_tags().collect(); tags.sort(); - assert_eq!( - tags, - vec![utag("abc"), utag("def"), stag(SyntheticTag::Waiting),] - ); + let mut exp = vec![ + utag("abc"), + utag("def"), + stag(SyntheticTag::Pending), + stag(SyntheticTag::Waiting), + ]; + exp.sort(); + assert_eq!(tags, exp); } #[test] @@ -433,7 +446,7 @@ mod test { // only "ok" is OK let tags: Vec<_> = task.get_tags().collect(); - assert_eq!(tags, vec![utag("ok")]); + assert_eq!(tags, vec![utag("ok"), stag(SyntheticTag::Pending)]); } fn count_taskmap(task: &TaskMut, f: fn(&(&String, &String)) -> bool) -> usize { @@ -493,6 +506,20 @@ mod test { }); } + #[test] + fn test_done() { + with_mut_task(|mut task| { + task.done().unwrap(); + assert_eq!(task.get_status(), Status::Completed); + assert!(task.has_tag(&stag(SyntheticTag::Completed))); + + // redundant call does nothing.. + task.done().unwrap(); + assert_eq!(task.get_status(), Status::Completed); + assert!(task.has_tag(&stag(SyntheticTag::Completed))); + }); + } + #[test] fn test_stop_multiple() { with_mut_task(|mut task| {