Centralize API for working set to a single struct

Rather than allow addressing tasks either by working set ID or uuid,
with attendant performance issues, this moves the API for the working
set to a single struct that just serves as a 1-1 mapping of indexes to
UUIDs.  It's up to the caller to use this information.
This commit is contained in:
Dustin J. Mitchell 2021-01-09 22:57:33 +00:00
parent d06f2e5aeb
commit 087769146e
7 changed files with 194 additions and 116 deletions

View file

@ -33,7 +33,11 @@ mod test {
execute(&mut w, &mut replica, modification).unwrap();
// check that the task appeared..
let task = replica.get_working_set_task(1).unwrap().unwrap();
let working_set = replica.working_set().unwrap();
let task = replica
.get_task(working_set.by_index(1).unwrap())
.unwrap()
.unwrap();
assert_eq!(task.get_description(), "my description");
assert_eq!(task.get_status(), Status::Pending);

View file

@ -12,6 +12,8 @@ pub(crate) fn execute<W: WriteColor>(
filter: Filter,
debug: bool,
) -> Fallible<()> {
let working_set = replica.working_set()?;
for task in filtered_tasks(replica, &filter)? {
let uuid = task.get_uuid();
@ -24,7 +26,7 @@ pub(crate) fn execute<W: WriteColor>(
}
} else {
t.add_row(row![b->"Uuid", uuid]);
if let Some(i) = replica.get_working_set_index(uuid)? {
if let Some(i) = working_set.by_uuid(uuid) {
t.add_row(row![b->"Id", i]);
}
t.add_row(row![b->"Description", task.get_description()]);

View file

@ -2,9 +2,9 @@ use crate::argparse::{Condition, Filter, TaskId};
use failure::Fallible;
use std::collections::HashSet;
use std::convert::TryInto;
use taskchampion::{Replica, Status, Tag, Task, Uuid};
use taskchampion::{Replica, Status, Tag, Task, Uuid, WorkingSet};
fn match_task(filter: &Filter, task: &Task, uuid: Uuid, working_set_id: Option<usize>) -> bool {
fn match_task(filter: &Filter, task: &Task, uuid: Uuid, working_set: &WorkingSet) -> bool {
for cond in &filter.conditions {
match cond {
Condition::HasTag(ref tag) => {
@ -29,6 +29,8 @@ fn match_task(filter: &Filter, task: &Task, uuid: Uuid, working_set_id: Option<u
Condition::IdList(ids) => {
let uuid_str = uuid.to_string();
let mut found = false;
let working_set_id = working_set.by_uuid(uuid);
for id in ids {
if match id {
TaskId::WorkingSetId(i) => Some(*i) == working_set_id,
@ -111,6 +113,8 @@ pub(super) fn filtered_tasks(
log::debug!("Applying filter {:?}", filter);
let working_set = replica.working_set()?;
// We will enumerate the universe of tasks for this filter, checking
// each resulting task with match_task
match universe_for_filter(filter) {
@ -123,7 +127,11 @@ pub(super) fn filtered_tasks(
let mut seen = HashSet::new();
for id in ids {
let task = match id {
TaskId::WorkingSetId(id) => replica.get_working_set_task(*id)?,
TaskId::WorkingSetId(id) => working_set
.by_index(*id)
.map(|uuid| replica.get_task(uuid))
.transpose()?
.flatten(),
TaskId::PartialUuid(_) => unreachable!(), // not present in absolute id list
TaskId::Uuid(id) => replica.get_task(*id)?,
};
@ -136,9 +144,7 @@ pub(super) fn filtered_tasks(
}
seen.insert(uuid);
let working_set_id = replica.get_working_set_index(uuid)?;
if match_task(filter, &task, uuid, working_set_id) {
if match_task(filter, &task, uuid, &working_set) {
res.push(task);
}
}
@ -149,19 +155,16 @@ pub(super) fn filtered_tasks(
Universe::AllTasks => {
log::debug!("Scanning all tasks in the task database");
for (uuid, task) in replica.all_tasks()?.drain() {
// Yikes, slow! https://github.com/djmitche/taskchampion/issues/108
let working_set_id = replica.get_working_set_index(uuid)?;
if match_task(filter, &task, uuid, working_set_id) {
if match_task(filter, &task, uuid, &working_set) {
res.push(task);
}
}
}
Universe::WorkingSet => {
log::debug!("Scanning only the working set (pending tasks)");
for (i, task) in replica.working_set()?.drain(..).enumerate() {
if let Some(task) = task {
let uuid = task.get_uuid();
if match_task(filter, &task, uuid, Some(i)) {
for (_, uuid) in working_set.iter() {
if let Some(task) = replica.get_task(uuid)? {
if match_task(filter, &task, uuid, &working_set) {
res.push(task);
}
}

View file

@ -6,35 +6,9 @@ use config::Config;
use failure::{format_err, Fallible};
use prettytable::{Row, Table};
use std::cmp::Ordering;
use taskchampion::{Replica, Task, Uuid};
use taskchampion::{Replica, Task, WorkingSet};
use termcolor::WriteColor;
// pending #123, this is a non-fallible way of looking up a task's working set index
struct WorkingSet(Vec<Option<Uuid>>);
impl WorkingSet {
fn new(replica: &mut Replica) -> Fallible<Self> {
let working_set = replica.working_set()?;
Ok(Self(
working_set
.iter()
.map(|opt| opt.as_ref().map(|t| t.get_uuid()))
.collect(),
))
}
fn index(&self, target: Uuid) -> Option<usize> {
for (i, uuid) in self.0.iter().enumerate() {
if let Some(uuid) = uuid {
if *uuid == target {
return Some(i);
}
}
}
None
}
}
/// Sort tasks for the given report.
fn sort_tasks(tasks: &mut Vec<Task>, report: &Report, working_set: &WorkingSet) {
tasks.sort_by(|a, b| {
@ -43,8 +17,8 @@ fn sort_tasks(tasks: &mut Vec<Task>, report: &Report, working_set: &WorkingSet)
SortBy::Id => {
let a_uuid = a.get_uuid();
let b_uuid = b.get_uuid();
let a_id = working_set.index(a_uuid);
let b_id = working_set.index(b_uuid);
let a_id = working_set.by_uuid(a_uuid);
let b_id = working_set.by_uuid(b_uuid);
println!("a_uuid {} -> a_id {:?}", a_uuid, a_id);
println!("b_uuid {} -> b_id {:?}", b_uuid, b_id);
match (a_id, b_id) {
@ -78,7 +52,7 @@ fn task_column(task: &Task, column: &Column, working_set: &WorkingSet) -> String
Property::Id => {
let uuid = task.get_uuid();
let mut id = uuid.to_string();
if let Some(i) = working_set.index(uuid) {
if let Some(i) = working_set.by_uuid(uuid) {
id = i.to_string();
}
id
@ -111,7 +85,7 @@ pub(super) fn display_report<W: WriteColor>(
filter: Filter,
) -> Fallible<()> {
let mut t = Table::new();
let working_set = WorkingSet::new(replica)?;
let working_set = replica.working_set()?;
// Get the report from settings
let mut report = Report::from_config(settings.get(&format!("reports.{}", report_name))?)
@ -151,7 +125,7 @@ mod test {
use crate::invocation::test::*;
use crate::report::Sort;
use std::convert::TryInto;
use taskchampion::Status;
use taskchampion::{Status, Uuid};
fn create_tasks(replica: &mut Replica) -> [Uuid; 3] {
let t1 = replica.new_task(Status::Pending, s!("A")).unwrap();
@ -172,7 +146,7 @@ mod test {
fn sorting_by_descr() {
let mut replica = test_replica();
create_tasks(&mut replica);
let working_set = WorkingSet::new(&mut replica).unwrap();
let working_set = replica.working_set().unwrap();
let mut report = Report {
sort: vec![Sort {
ascending: true,
@ -199,7 +173,7 @@ mod test {
fn sorting_by_id() {
let mut replica = test_replica();
create_tasks(&mut replica);
let working_set = WorkingSet::new(&mut replica).unwrap();
let working_set = replica.working_set().unwrap();
let mut report = Report {
sort: vec![Sort {
ascending: true,
@ -226,7 +200,7 @@ mod test {
fn sorting_by_uuid() {
let mut replica = test_replica();
let uuids = create_tasks(&mut replica);
let working_set = WorkingSet::new(&mut replica).unwrap();
let working_set = replica.working_set().unwrap();
let report = Report {
sort: vec![Sort {
ascending: true,
@ -254,7 +228,7 @@ mod test {
.add_tag(&("second".try_into().unwrap()))
.unwrap();
let working_set = WorkingSet::new(&mut replica).unwrap();
let working_set = replica.working_set().unwrap();
let report = Report {
sort: vec![
Sort {
@ -280,9 +254,9 @@ mod test {
fn task_column_id() {
let mut replica = test_replica();
let uuids = create_tasks(&mut replica);
let working_set = WorkingSet::new(&mut replica).unwrap();
let working_set = replica.working_set().unwrap();
let task = replica.get_working_set_task(1).unwrap().unwrap();
let task = replica.get_task(uuids[0]).unwrap().unwrap();
let column = Column {
label: s!(""),
property: Property::Id,
@ -301,10 +275,10 @@ mod test {
#[test]
fn task_column_uuid() {
let mut replica = test_replica();
create_tasks(&mut replica);
let working_set = WorkingSet::new(&mut replica).unwrap();
let uuids = create_tasks(&mut replica);
let working_set = replica.working_set().unwrap();
let task = replica.get_working_set_task(1).unwrap().unwrap();
let task = replica.get_task(uuids[0]).unwrap().unwrap();
let column = Column {
label: s!(""),
property: Property::Uuid,
@ -319,7 +293,7 @@ mod test {
fn task_column_active() {
let mut replica = test_replica();
let uuids = create_tasks(&mut replica);
let working_set = WorkingSet::new(&mut replica).unwrap();
let working_set = replica.working_set().unwrap();
// make task A active
replica
@ -335,19 +309,19 @@ mod test {
property: Property::Active,
};
let task = replica.get_working_set_task(1).unwrap().unwrap();
let task = replica.get_task(uuids[0]).unwrap().unwrap();
assert_eq!(task_column(&task, &column, &working_set), s!("*"));
let task = replica.get_working_set_task(2).unwrap().unwrap();
let task = replica.get_task(uuids[2]).unwrap().unwrap();
assert_eq!(task_column(&task, &column, &working_set), s!(""));
}
#[test]
fn task_column_description() {
let mut replica = test_replica();
create_tasks(&mut replica);
let working_set = WorkingSet::new(&mut replica).unwrap();
let uuids = create_tasks(&mut replica);
let working_set = replica.working_set().unwrap();
let task = replica.get_working_set_task(2).unwrap().unwrap();
let task = replica.get_task(uuids[2]).unwrap().unwrap();
let column = Column {
label: s!(""),
property: Property::Description,
@ -359,7 +333,7 @@ mod test {
fn task_column_tags() {
let mut replica = test_replica();
let uuids = create_tasks(&mut replica);
let working_set = WorkingSet::new(&mut replica).unwrap();
let working_set = replica.working_set().unwrap();
// add some tags to task A
let mut t1 = replica
@ -375,9 +349,9 @@ mod test {
property: Property::Tags,
};
let task = replica.get_working_set_task(1).unwrap().unwrap();
let task = replica.get_task(uuids[0]).unwrap().unwrap();
assert_eq!(task_column(&task, &column, &working_set), s!("+bar +foo"));
let task = replica.get_working_set_task(2).unwrap().unwrap();
let task = replica.get_task(uuids[2]).unwrap().unwrap();
assert_eq!(task_column(&task, &column, &working_set), s!(""));
}
}