Merge pull request #309 from djmitche/issue286

use TW's semantics for `start`
This commit is contained in:
Dustin J. Mitchell 2021-10-25 09:21:11 -04:00 committed by GitHub
commit ae80cbef2a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 28 additions and 100 deletions

View file

@ -0,0 +1 @@
- The details of how task start/stop is represented have changed. Any existing tasks will all be treated as inactive (stopped).

View file

@ -30,7 +30,7 @@ The following keys, and key formats, are defined:
* `status` - one of `P` for a pending task (the default), `C` for completed or `D` for deleted * `status` - one of `P` for a pending task (the default), `C` for completed or `D` for deleted
* `description` - the one-line summary of the task * `description` - the one-line summary of the task
* `modified` - the time of the last modification of this task * `modified` - the time of the last modification of this task
* `start.<timestamp>` - either an empty string (representing work on the task to the task that has not been stopped) or a timestamp (representing the time that work stopped) * `start` - the most recent time at which this task was started (a task with no `start` key is not active)
* `tag.<tag>` - indicates this task has tag `<tag>` (value is an empty string) * `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 * `wait` - indicates the time before which this task should be hidden, as it is not actionable

View file

@ -100,9 +100,7 @@ impl Task {
/// Determine whether this task is active -- that is, that it has been started /// Determine whether this task is active -- that is, that it has been started
/// and not stopped. /// and not stopped.
pub fn is_active(&self) -> bool { pub fn is_active(&self) -> bool {
self.taskmap self.taskmap.contains_key("start")
.iter()
.any(|(k, v)| k.starts_with("start.") && v.is_empty())
} }
/// Determine whether a given synthetic tag is present on this task. All other /// Determine whether a given synthetic tag is present on this task. All other
@ -192,31 +190,18 @@ impl<'r> TaskMut<'r> {
self.set_timestamp("modified", Some(modified)) self.set_timestamp("modified", Some(modified))
} }
/// Start the task by creating "start.<timestamp": "", if the task is not already /// Start the task by creating "start": "<timestamp>", if the task is not already
/// active. /// active.
pub fn start(&mut self) -> anyhow::Result<()> { pub fn start(&mut self) -> anyhow::Result<()> {
if self.is_active() { if self.is_active() {
return Ok(()); return Ok(());
} }
let k = format!("start.{}", Utc::now().timestamp()); self.set_timestamp("start", Some(Utc::now()))
self.set_string(k, Some(String::from("")))
} }
/// Stop the task by adding the current timestamp to all un-resolved "start.<timestamp>" keys. /// Stop the task by removing the `start` key
pub fn stop(&mut self) -> anyhow::Result<()> { pub fn stop(&mut self) -> anyhow::Result<()> {
let keys = self self.set_timestamp("start", None)
.taskmap
.iter()
.filter(|(k, v)| k.starts_with("start.") && v.is_empty())
.map(|(k, _)| k)
.cloned()
.collect::<Vec<_>>();
let now = Utc::now();
for key in keys {
println!("{}", key);
self.set_timestamp(&key, Some(now))?;
}
Ok(())
} }
/// Mark this task as complete /// Mark this task as complete
@ -340,10 +325,10 @@ mod test {
} }
#[test] #[test]
fn test_is_active() { fn test_is_active_active() {
let task = Task::new( let task = Task::new(
Uuid::new_v4(), Uuid::new_v4(),
vec![(String::from("start.1234"), String::from(""))] vec![(String::from("start"), String::from("1234"))]
.drain(..) .drain(..)
.collect(), .collect(),
); );
@ -352,14 +337,8 @@ mod test {
} }
#[test] #[test]
fn test_is_active_stopped() { fn test_is_active_inactive() {
let task = Task::new( let task = Task::new(Uuid::new_v4(), Default::default());
Uuid::new_v4(),
vec![(String::from("start.1234"), String::from("1235"))]
.drain(..)
.collect(),
);
assert!(!task.is_active()); assert!(!task.is_active());
} }
@ -405,7 +384,7 @@ mod test {
Uuid::new_v4(), Uuid::new_v4(),
vec![ vec![
(String::from("tag.abc"), String::from("")), (String::from("tag.abc"), String::from("")),
(String::from("start.1234"), String::from("")), (String::from("start"), String::from("1234")),
] ]
.drain(..) .drain(..)
.collect(), .collect(),
@ -463,35 +442,21 @@ mod test {
assert_eq!(tags, vec![utag("ok"), stag(SyntheticTag::Pending)]); assert_eq!(tags, vec![utag("ok"), stag(SyntheticTag::Pending)]);
} }
fn count_taskmap(task: &TaskMut, f: fn(&(&String, &String)) -> bool) -> usize {
task.taskmap.iter().filter(f).count()
}
#[test] #[test]
fn test_start() { fn test_start() {
with_mut_task(|mut task| { with_mut_task(|mut task| {
task.start().unwrap(); task.start().unwrap();
assert_eq!( assert!(task.taskmap.contains_key("start"));
count_taskmap(&task, |(k, v)| k.starts_with("start.") && v.is_empty()),
1
);
task.reload().unwrap(); task.reload().unwrap();
assert_eq!( assert!(task.taskmap.contains_key("start"));
count_taskmap(&task, |(k, v)| k.starts_with("start.") && v.is_empty()),
1
);
// second start doesn't change anything.. // second start doesn't change anything..
task.start().unwrap(); task.start().unwrap();
assert_eq!( assert!(task.taskmap.contains_key("start"));
count_taskmap(&task, |(k, v)| k.starts_with("start.") && v.is_empty()),
1
);
task.reload().unwrap(); task.reload().unwrap();
assert_eq!( assert!(task.taskmap.contains_key("start"));
count_taskmap(&task, |(k, v)| k.starts_with("start.") && v.is_empty()),
1
);
}); });
} }
@ -500,23 +465,17 @@ mod test {
with_mut_task(|mut task| { with_mut_task(|mut task| {
task.start().unwrap(); task.start().unwrap();
task.stop().unwrap(); task.stop().unwrap();
assert_eq!( assert!(!task.taskmap.contains_key("start"));
count_taskmap(&task, |(k, v)| k.starts_with("start.") && v.is_empty()),
0
);
assert_eq!(
count_taskmap(&task, |(k, v)| k.starts_with("start.") && !v.is_empty()),
1
);
task.reload().unwrap(); task.reload().unwrap();
assert_eq!( assert!(!task.taskmap.contains_key("start"));
count_taskmap(&task, |(k, v)| k.starts_with("start.") && v.is_empty()),
0 // redundant call does nothing..
); task.stop().unwrap();
assert_eq!( assert!(!task.taskmap.contains_key("start"));
count_taskmap(&task, |(k, v)| k.starts_with("start.") && !v.is_empty()),
1 task.reload().unwrap();
); assert!(!task.taskmap.contains_key("start"));
}); });
} }
@ -534,38 +493,6 @@ mod test {
}); });
} }
#[test]
fn test_stop_multiple() {
with_mut_task(|mut task| {
// simulate a task that has (through the synchronization process) been started twice
task.task
.taskmap
.insert(String::from("start.1234"), String::from(""));
task.task
.taskmap
.insert(String::from("start.5678"), String::from(""));
task.stop().unwrap();
assert_eq!(
count_taskmap(&task, |(k, v)| k.starts_with("start.") && v.is_empty()),
0
);
assert_eq!(
count_taskmap(&task, |(k, v)| k.starts_with("start.") && !v.is_empty()),
2
);
task.reload().unwrap();
assert_eq!(
count_taskmap(&task, |(k, v)| k.starts_with("start.") && v.is_empty()),
0
);
assert_eq!(
count_taskmap(&task, |(k, v)| k.starts_with("start.") && !v.is_empty()),
2
);
});
}
#[test] #[test]
fn test_add_tags() { fn test_add_tags() {
with_mut_task(|mut task| { with_mut_task(|mut task| {