mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-08-20 13:23:08 +02:00
Merge pull request #330 from djmitche/issue327
Support an 'end' key in task maps
This commit is contained in:
commit
8576e7ffa7
3 changed files with 93 additions and 4 deletions
|
@ -17,6 +17,12 @@ The result of this reconciliation will be `oldtag,newtag2`, while the user almos
|
|||
|
||||
The key names given below avoid this issue, allowing user updates such as adding a tag or deleting a dependency to be represented in a single `Update` operation.
|
||||
|
||||
## Validity
|
||||
|
||||
_Any_ key/value map is a valid task.
|
||||
Consumers of task data must make a best effort to interpret any map, even if it contains apparently contradictory information.
|
||||
For example, a task with status "completed" but no "end" key present should be interpreted as completed at an unknown time.
|
||||
|
||||
## Representations
|
||||
|
||||
Integers are stored in decimal notation.
|
||||
|
@ -31,6 +37,7 @@ The following keys, and key formats, are defined:
|
|||
* `description` - the one-line summary of the task
|
||||
* `modified` - the time of the last modification of this task
|
||||
* `start` - the most recent time at which this task was started (a task with no `start` key is not active)
|
||||
* `end` - if present, the time at which this task was completed or deleted (note that this key may not agree with `status`: it may be present for a pending task, or absent for a deleted or completed task)
|
||||
* `tag_<tag>` - indicates this task has tag `<tag>` (value is an empty string)
|
||||
* `wait` - indicates the time before which this task should be hidden, as it is not actionable
|
||||
* `entry` - the time at which the task was created
|
||||
|
|
|
@ -241,7 +241,9 @@ mod tests {
|
|||
..
|
||||
} = op
|
||||
{
|
||||
if property == "modified" || property == "entry" {
|
||||
// rewrite automatically-created dates to "just-now" for ease
|
||||
// of testing
|
||||
if property == "modified" || property == "end" || property == "entry" {
|
||||
if value.is_some() {
|
||||
value = Some("just-now".into());
|
||||
}
|
||||
|
@ -311,6 +313,13 @@ mod tests {
|
|||
value: Some("past tense".into()),
|
||||
timestamp: now,
|
||||
},
|
||||
ReplicaOp::Update {
|
||||
uuid: t.get_uuid(),
|
||||
property: "end".into(),
|
||||
old_value: None,
|
||||
value: Some("just-now".into()),
|
||||
timestamp: now,
|
||||
},
|
||||
ReplicaOp::Update {
|
||||
uuid: t.get_uuid(),
|
||||
property: "status".into(),
|
||||
|
|
|
@ -57,6 +57,7 @@ enum Prop {
|
|||
Start,
|
||||
Status,
|
||||
Wait,
|
||||
End,
|
||||
Entry,
|
||||
}
|
||||
|
||||
|
@ -271,10 +272,23 @@ impl<'r> TaskMut<'r> {
|
|||
/// Set the task's status. This also adds the task to the working set if the
|
||||
/// new status puts it in that set.
|
||||
pub fn set_status(&mut self, status: Status) -> anyhow::Result<()> {
|
||||
if status == Status::Pending {
|
||||
match status {
|
||||
Status::Pending => {
|
||||
// clear "end" when a task becomes "pending"
|
||||
if self.taskmap.contains_key(Prop::End.as_ref()) {
|
||||
self.set_timestamp(Prop::End.as_ref(), None)?;
|
||||
}
|
||||
let uuid = self.uuid;
|
||||
self.replica.add_to_working_set(uuid)?;
|
||||
}
|
||||
Status::Completed | Status::Deleted => {
|
||||
// set "end" when a task is deleted or completed
|
||||
if !self.taskmap.contains_key(Prop::End.as_ref()) {
|
||||
self.set_timestamp(Prop::End.as_ref(), Some(Utc::now()))?;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
self.set_string(
|
||||
Prop::Status.as_ref(),
|
||||
Some(String::from(status.to_taskmap())),
|
||||
|
@ -316,6 +330,14 @@ impl<'r> TaskMut<'r> {
|
|||
self.set_status(Status::Completed)
|
||||
}
|
||||
|
||||
/// Mark this task as deleted.
|
||||
///
|
||||
/// Note that this does not delete the task. It merely marks the task as
|
||||
/// deleted.
|
||||
pub fn delete(&mut self) -> anyhow::Result<()> {
|
||||
self.set_status(Status::Deleted)
|
||||
}
|
||||
|
||||
/// 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() {
|
||||
|
@ -688,6 +710,41 @@ mod test {
|
|||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_status_pending() {
|
||||
with_mut_task(|mut task| {
|
||||
task.done().unwrap();
|
||||
|
||||
task.set_status(Status::Pending).unwrap();
|
||||
assert_eq!(task.get_status(), Status::Pending);
|
||||
assert!(!task.taskmap.contains_key("end"));
|
||||
assert!(task.has_tag(&stag(SyntheticTag::Pending)));
|
||||
assert!(!task.has_tag(&stag(SyntheticTag::Completed)));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_status_completed() {
|
||||
with_mut_task(|mut task| {
|
||||
task.set_status(Status::Completed).unwrap();
|
||||
assert_eq!(task.get_status(), Status::Completed);
|
||||
assert!(task.taskmap.contains_key("end"));
|
||||
assert!(!task.has_tag(&stag(SyntheticTag::Pending)));
|
||||
assert!(task.has_tag(&stag(SyntheticTag::Completed)));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_status_deleted() {
|
||||
with_mut_task(|mut task| {
|
||||
task.set_status(Status::Deleted).unwrap();
|
||||
assert_eq!(task.get_status(), Status::Deleted);
|
||||
assert!(task.taskmap.contains_key("end"));
|
||||
assert!(!task.has_tag(&stag(SyntheticTag::Pending)));
|
||||
assert!(!task.has_tag(&stag(SyntheticTag::Completed)));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_start() {
|
||||
with_mut_task(|mut task| {
|
||||
|
@ -730,6 +787,7 @@ mod test {
|
|||
with_mut_task(|mut task| {
|
||||
task.done().unwrap();
|
||||
assert_eq!(task.get_status(), Status::Completed);
|
||||
assert!(task.taskmap.contains_key("end"));
|
||||
assert!(task.has_tag(&stag(SyntheticTag::Completed)));
|
||||
|
||||
// redundant call does nothing..
|
||||
|
@ -739,6 +797,21 @@ mod test {
|
|||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete() {
|
||||
with_mut_task(|mut task| {
|
||||
task.delete().unwrap();
|
||||
assert_eq!(task.get_status(), Status::Deleted);
|
||||
assert!(task.taskmap.contains_key("end"));
|
||||
assert!(!task.has_tag(&stag(SyntheticTag::Completed)));
|
||||
|
||||
// redundant call does nothing..
|
||||
task.delete().unwrap();
|
||||
assert_eq!(task.get_status(), Status::Deleted);
|
||||
assert!(!task.has_tag(&stag(SyntheticTag::Completed)));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_tags() {
|
||||
with_mut_task(|mut task| {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue