mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-08-23 14:36:44 +02:00
Merge pull request #126 from djmitche/issue119
implement generic report generation
This commit is contained in:
commit
db85ff86a1
7 changed files with 484 additions and 50 deletions
|
@ -22,7 +22,7 @@ pub(crate) use args::TaskId;
|
||||||
pub(crate) use command::Command;
|
pub(crate) use command::Command;
|
||||||
pub(crate) use filter::{Condition, Filter, Universe};
|
pub(crate) use filter::{Condition, Filter, Universe};
|
||||||
pub(crate) use modification::{DescriptionMod, Modification};
|
pub(crate) use modification::{DescriptionMod, Modification};
|
||||||
pub(crate) use report::Report;
|
pub(crate) use report::{Column, Property, Report, Sort, SortBy};
|
||||||
pub(crate) use subcommand::Subcommand;
|
pub(crate) use subcommand::Subcommand;
|
||||||
|
|
||||||
use crate::usage::Usage;
|
use crate::usage::Usage;
|
||||||
|
|
|
@ -1,33 +1,68 @@
|
||||||
use super::{ArgList, Filter};
|
use super::Filter;
|
||||||
use nom::IResult;
|
|
||||||
|
|
||||||
/// A report specifies a filter as well as a sort order and information about which
|
/// A report specifies a filter as well as a sort order and information about which
|
||||||
/// task attributes to display
|
/// task attributes to display
|
||||||
#[derive(Debug, PartialEq, Default)]
|
#[derive(Clone, Debug, PartialEq, Default)]
|
||||||
pub(crate) struct Report {
|
pub(crate) struct Report {
|
||||||
|
/// Columns to display in this report
|
||||||
|
pub columns: Vec<Column>,
|
||||||
|
/// Sort order for this report
|
||||||
|
pub sort: Vec<Sort>,
|
||||||
|
/// Filter selecting tasks for this report
|
||||||
pub filter: Filter,
|
pub filter: Filter,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Report {
|
/// A column to display in a report
|
||||||
pub(super) fn parse(input: ArgList) -> IResult<ArgList, Report> {
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
let (input, filter) = Filter::parse(input)?;
|
pub(crate) struct Column {
|
||||||
Ok((input, Report { filter }))
|
/// The label for this column
|
||||||
}
|
pub label: String,
|
||||||
|
|
||||||
|
/// The property to display
|
||||||
|
pub property: Property,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
/// Task property to display in a report
|
||||||
mod test {
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
use super::*;
|
#[allow(dead_code)]
|
||||||
|
pub(crate) enum Property {
|
||||||
|
/// The task's ID, either working-set index or Uuid if no in the working set
|
||||||
|
Id,
|
||||||
|
|
||||||
#[test]
|
/// The task's full UUID
|
||||||
fn test_empty() {
|
Uuid,
|
||||||
let (input, report) = Report::parse(argv![]).unwrap();
|
|
||||||
assert_eq!(input.len(), 0);
|
/// Whether the task is active or not
|
||||||
assert_eq!(
|
Active,
|
||||||
report,
|
|
||||||
Report {
|
/// The task's description
|
||||||
..Default::default()
|
Description,
|
||||||
|
|
||||||
|
/// The task's tags
|
||||||
|
Tags,
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
/// A sorting criterion for a sort operation.
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub(crate) struct Sort {
|
||||||
|
/// True if the sort should be "ascending" (a -> z, 0 -> 9, etc.)
|
||||||
|
pub ascending: bool,
|
||||||
|
|
||||||
|
/// The property to sort on
|
||||||
|
pub sort_by: SortBy,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Task property to sort by
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) enum SortBy {
|
||||||
|
/// The task's ID, either working-set index or a UUID prefix; working
|
||||||
|
/// set tasks sort before others.
|
||||||
|
Id,
|
||||||
|
|
||||||
|
/// The task's full UUID
|
||||||
|
Uuid,
|
||||||
|
|
||||||
|
/// The task's description
|
||||||
|
Description,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use super::args::*;
|
use super::args::*;
|
||||||
use super::{ArgList, DescriptionMod, Filter, Modification, Report};
|
use super::{
|
||||||
|
ArgList, Column, DescriptionMod, Filter, Modification, Property, Report, Sort, SortBy,
|
||||||
|
};
|
||||||
use crate::usage;
|
use crate::usage;
|
||||||
use nom::{branch::alt, combinator::*, sequence::*, IResult};
|
use nom::{branch::alt, combinator::*, sequence::*, IResult};
|
||||||
use taskchampion::Status;
|
use taskchampion::Status;
|
||||||
|
@ -252,12 +254,45 @@ impl Modify {
|
||||||
struct List;
|
struct List;
|
||||||
|
|
||||||
impl List {
|
impl List {
|
||||||
|
// temporary
|
||||||
|
fn default_report() -> Report {
|
||||||
|
Report {
|
||||||
|
columns: vec![
|
||||||
|
Column {
|
||||||
|
label: "Id".to_owned(),
|
||||||
|
property: Property::Id,
|
||||||
|
},
|
||||||
|
Column {
|
||||||
|
label: "Description".to_owned(),
|
||||||
|
property: Property::Description,
|
||||||
|
},
|
||||||
|
Column {
|
||||||
|
label: "Active".to_owned(),
|
||||||
|
property: Property::Active,
|
||||||
|
},
|
||||||
|
Column {
|
||||||
|
label: "Tags".to_owned(),
|
||||||
|
property: Property::Tags,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
sort: vec![Sort {
|
||||||
|
ascending: false,
|
||||||
|
sort_by: SortBy::Uuid,
|
||||||
|
}],
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn parse(input: ArgList) -> IResult<ArgList, Subcommand> {
|
fn parse(input: ArgList) -> IResult<ArgList, Subcommand> {
|
||||||
fn to_subcommand(input: (Report, &str)) -> Result<Subcommand, ()> {
|
fn to_subcommand(input: (Filter, &str)) -> Result<Subcommand, ()> {
|
||||||
Ok(Subcommand::List { report: input.0 })
|
let report = Report {
|
||||||
|
filter: input.0,
|
||||||
|
..List::default_report()
|
||||||
|
};
|
||||||
|
Ok(Subcommand::List { report })
|
||||||
}
|
}
|
||||||
map_res(
|
map_res(
|
||||||
pair(Report::parse, arg_matching(literal("list"))),
|
pair(Filter::parse, arg_matching(literal("list"))),
|
||||||
to_subcommand,
|
to_subcommand,
|
||||||
)(input)
|
)(input)
|
||||||
}
|
}
|
||||||
|
@ -602,7 +637,7 @@ mod test {
|
||||||
fn test_list() {
|
fn test_list() {
|
||||||
let subcommand = Subcommand::List {
|
let subcommand = Subcommand::List {
|
||||||
report: Report {
|
report: Report {
|
||||||
..Default::default()
|
..List::default_report()
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -619,6 +654,7 @@ mod test {
|
||||||
universe: Universe::for_ids(vec![12, 13]),
|
universe: Universe::for_ids(vec![12, 13]),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
..List::default_report()
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
use crate::argparse::Report;
|
use crate::argparse::Report;
|
||||||
use crate::invocation::filtered_tasks;
|
use crate::invocation::display_report;
|
||||||
use crate::table;
|
|
||||||
use failure::Fallible;
|
use failure::Fallible;
|
||||||
use prettytable::{cell, row, Table};
|
|
||||||
use taskchampion::Replica;
|
use taskchampion::Replica;
|
||||||
use termcolor::WriteColor;
|
use termcolor::WriteColor;
|
||||||
|
|
||||||
|
@ -11,29 +9,13 @@ pub(crate) fn execute<W: WriteColor>(
|
||||||
replica: &mut Replica,
|
replica: &mut Replica,
|
||||||
report: Report,
|
report: Report,
|
||||||
) -> Fallible<()> {
|
) -> Fallible<()> {
|
||||||
let mut t = Table::new();
|
display_report(w, replica, &report)
|
||||||
t.set_format(table::format());
|
|
||||||
t.set_titles(row![b->"id", b->"act", b->"description"]);
|
|
||||||
for task in filtered_tasks(replica, &report.filter)? {
|
|
||||||
let uuid = task.get_uuid();
|
|
||||||
let mut id = uuid.to_string();
|
|
||||||
if let Some(i) = replica.get_working_set_index(&uuid)? {
|
|
||||||
id = i.to_string();
|
|
||||||
}
|
|
||||||
let active = match task.is_active() {
|
|
||||||
true => "*",
|
|
||||||
false => "",
|
|
||||||
};
|
|
||||||
t.add_row(row![id, active, task.get_description()]);
|
|
||||||
}
|
|
||||||
t.print(w)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::argparse::Filter;
|
use crate::argparse::{Column, Filter, Property};
|
||||||
use crate::invocation::test::*;
|
use crate::invocation::test::*;
|
||||||
use taskchampion::Status;
|
use taskchampion::Status;
|
||||||
|
|
||||||
|
@ -47,6 +29,11 @@ mod test {
|
||||||
filter: Filter {
|
filter: Filter {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
columns: vec![Column {
|
||||||
|
label: "Description".to_owned(),
|
||||||
|
property: Property::Description,
|
||||||
|
}],
|
||||||
|
..Default::default()
|
||||||
};
|
};
|
||||||
execute(&mut w, &mut replica, report).unwrap();
|
execute(&mut w, &mut replica, report).unwrap();
|
||||||
assert!(w.into_string().contains("my task"));
|
assert!(w.into_string().contains("my task"));
|
||||||
|
|
|
@ -9,12 +9,14 @@ use termcolor::{ColorChoice, StandardStream};
|
||||||
mod cmd;
|
mod cmd;
|
||||||
mod filter;
|
mod filter;
|
||||||
mod modify;
|
mod modify;
|
||||||
|
mod report;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test;
|
mod test;
|
||||||
|
|
||||||
use filter::filtered_tasks;
|
use filter::filtered_tasks;
|
||||||
use modify::apply_modification;
|
use modify::apply_modification;
|
||||||
|
use report::display_report;
|
||||||
|
|
||||||
/// Invoke the given Command in the context of the given settings
|
/// Invoke the given Command in the context of the given settings
|
||||||
#[allow(clippy::needless_return)]
|
#[allow(clippy::needless_return)]
|
||||||
|
|
374
cli/src/invocation/report.rs
Normal file
374
cli/src/invocation/report.rs
Normal file
|
@ -0,0 +1,374 @@
|
||||||
|
use crate::argparse::{Column, Property, Report, SortBy};
|
||||||
|
use crate::invocation::filtered_tasks;
|
||||||
|
use crate::table;
|
||||||
|
use failure::Fallible;
|
||||||
|
use prettytable::{Row, Table};
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
use taskchampion::{Replica, Task, Uuid};
|
||||||
|
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| {
|
||||||
|
for s in &report.sort {
|
||||||
|
let ord = match s.sort_by {
|
||||||
|
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);
|
||||||
|
println!("a_uuid {} -> a_id {:?}", a_uuid, a_id);
|
||||||
|
println!("b_uuid {} -> b_id {:?}", b_uuid, b_id);
|
||||||
|
match (a_id, b_id) {
|
||||||
|
(Some(a_id), Some(b_id)) => a_id.cmp(&b_id),
|
||||||
|
(Some(_), None) => Ordering::Less,
|
||||||
|
(None, Some(_)) => Ordering::Greater,
|
||||||
|
(None, None) => a_uuid.cmp(b_uuid),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SortBy::Uuid => a.get_uuid().cmp(b.get_uuid()),
|
||||||
|
SortBy::Description => a.get_description().cmp(b.get_description()),
|
||||||
|
};
|
||||||
|
// If this sort property is equal, go on to the next..
|
||||||
|
if ord == Ordering::Equal {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Reverse order if not ascending
|
||||||
|
if s.ascending {
|
||||||
|
return ord;
|
||||||
|
} else {
|
||||||
|
return ord.reverse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ordering::Equal
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate the string representation for the given task and column.
|
||||||
|
fn task_column(task: &Task, column: &Column, working_set: &WorkingSet) -> String {
|
||||||
|
match column.property {
|
||||||
|
Property::Id => {
|
||||||
|
let uuid = task.get_uuid();
|
||||||
|
let mut id = uuid.to_string();
|
||||||
|
if let Some(i) = working_set.index(uuid) {
|
||||||
|
id = i.to_string();
|
||||||
|
}
|
||||||
|
id
|
||||||
|
}
|
||||||
|
Property::Uuid => {
|
||||||
|
let uuid = task.get_uuid();
|
||||||
|
uuid.to_string()
|
||||||
|
}
|
||||||
|
Property::Active => match task.is_active() {
|
||||||
|
true => "*".to_owned(),
|
||||||
|
false => "".to_owned(),
|
||||||
|
},
|
||||||
|
Property::Description => task.get_description().to_owned(),
|
||||||
|
Property::Tags => {
|
||||||
|
let mut tags = task
|
||||||
|
.get_tags()
|
||||||
|
.map(|t| format!("+{}", t))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
tags.sort();
|
||||||
|
tags.join(" ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn display_report<W: WriteColor>(
|
||||||
|
w: &mut W,
|
||||||
|
replica: &mut Replica,
|
||||||
|
report: &Report,
|
||||||
|
) -> Fallible<()> {
|
||||||
|
let mut t = Table::new();
|
||||||
|
|
||||||
|
let working_set = WorkingSet::new(replica)?;
|
||||||
|
|
||||||
|
// Get the tasks from the filter
|
||||||
|
let mut tasks: Vec<_> = filtered_tasks(replica, &report.filter)?.collect();
|
||||||
|
|
||||||
|
// ..sort them as desired
|
||||||
|
sort_tasks(&mut tasks, report, &working_set);
|
||||||
|
|
||||||
|
// ..set up the column titles
|
||||||
|
t.set_format(table::format());
|
||||||
|
t.set_titles(report.columns.iter().map(|col| col.label.clone()).into());
|
||||||
|
|
||||||
|
// ..insert the data
|
||||||
|
for task in &tasks {
|
||||||
|
let row: Row = report
|
||||||
|
.columns
|
||||||
|
.iter()
|
||||||
|
.map(|col| task_column(task, col, &working_set))
|
||||||
|
.collect::<Row>();
|
||||||
|
t.add_row(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ..and display it
|
||||||
|
t.print(w)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use crate::argparse::{Column, Property, Report, Sort, SortBy};
|
||||||
|
use crate::invocation::test::*;
|
||||||
|
use std::convert::TryInto;
|
||||||
|
use taskchampion::Status;
|
||||||
|
|
||||||
|
fn create_tasks(replica: &mut Replica) -> [Uuid; 3] {
|
||||||
|
let t1 = replica.new_task(Status::Pending, s!("A")).unwrap();
|
||||||
|
let t2 = replica.new_task(Status::Pending, s!("B")).unwrap();
|
||||||
|
let t3 = replica.new_task(Status::Pending, s!("C")).unwrap();
|
||||||
|
|
||||||
|
// t2 is comleted and not in the working set
|
||||||
|
let mut t2 = t2.into_mut(replica);
|
||||||
|
t2.set_status(Status::Completed).unwrap();
|
||||||
|
let t2 = t2.into_immut();
|
||||||
|
|
||||||
|
replica.gc().unwrap();
|
||||||
|
|
||||||
|
[*t1.get_uuid(), *t2.get_uuid(), *t3.get_uuid()]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sorting_by_descr() {
|
||||||
|
let mut replica = test_replica();
|
||||||
|
create_tasks(&mut replica);
|
||||||
|
let working_set = WorkingSet::new(&mut replica).unwrap();
|
||||||
|
let mut report = Report {
|
||||||
|
sort: vec![Sort {
|
||||||
|
ascending: true,
|
||||||
|
sort_by: SortBy::Description,
|
||||||
|
}],
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
// ascending
|
||||||
|
let mut tasks: Vec<_> = replica.all_tasks().unwrap().values().cloned().collect();
|
||||||
|
sort_tasks(&mut tasks, &report, &working_set);
|
||||||
|
let descriptions: Vec<_> = tasks.iter().map(|t| t.get_description()).collect();
|
||||||
|
assert_eq!(descriptions, vec!["A", "B", "C"]);
|
||||||
|
|
||||||
|
// ascending
|
||||||
|
report.sort[0].ascending = false;
|
||||||
|
let mut tasks: Vec<_> = replica.all_tasks().unwrap().values().cloned().collect();
|
||||||
|
sort_tasks(&mut tasks, &report, &working_set);
|
||||||
|
let descriptions: Vec<_> = tasks.iter().map(|t| t.get_description()).collect();
|
||||||
|
assert_eq!(descriptions, vec!["C", "B", "A"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sorting_by_id() {
|
||||||
|
let mut replica = test_replica();
|
||||||
|
create_tasks(&mut replica);
|
||||||
|
let working_set = WorkingSet::new(&mut replica).unwrap();
|
||||||
|
let mut report = Report {
|
||||||
|
sort: vec![Sort {
|
||||||
|
ascending: true,
|
||||||
|
sort_by: SortBy::Id,
|
||||||
|
}],
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
// ascending
|
||||||
|
let mut tasks: Vec<_> = replica.all_tasks().unwrap().values().cloned().collect();
|
||||||
|
sort_tasks(&mut tasks, &report, &working_set);
|
||||||
|
let descriptions: Vec<_> = tasks.iter().map(|t| t.get_description()).collect();
|
||||||
|
assert_eq!(descriptions, vec!["A", "C", "B"]);
|
||||||
|
|
||||||
|
// ascending
|
||||||
|
report.sort[0].ascending = false;
|
||||||
|
let mut tasks: Vec<_> = replica.all_tasks().unwrap().values().cloned().collect();
|
||||||
|
sort_tasks(&mut tasks, &report, &working_set);
|
||||||
|
let descriptions: Vec<_> = tasks.iter().map(|t| t.get_description()).collect();
|
||||||
|
assert_eq!(descriptions, vec!["B", "C", "A"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 report = Report {
|
||||||
|
sort: vec![Sort {
|
||||||
|
ascending: true,
|
||||||
|
sort_by: SortBy::Uuid,
|
||||||
|
}],
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut tasks: Vec<_> = replica.all_tasks().unwrap().values().cloned().collect();
|
||||||
|
sort_tasks(&mut tasks, &report, &working_set);
|
||||||
|
let got_uuids: Vec<_> = tasks.iter().map(|t| *t.get_uuid()).collect();
|
||||||
|
let mut exp_uuids = uuids.to_vec();
|
||||||
|
exp_uuids.sort();
|
||||||
|
assert_eq!(got_uuids, exp_uuids);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sorting_by_multiple() {
|
||||||
|
let mut replica = test_replica();
|
||||||
|
create_tasks(&mut replica);
|
||||||
|
|
||||||
|
// make a second task named A with a larger ID than the first
|
||||||
|
let t = replica.new_task(Status::Pending, s!("A")).unwrap();
|
||||||
|
t.into_mut(&mut replica)
|
||||||
|
.add_tag(&("second".try_into().unwrap()))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let working_set = WorkingSet::new(&mut replica).unwrap();
|
||||||
|
let report = Report {
|
||||||
|
sort: vec![
|
||||||
|
Sort {
|
||||||
|
ascending: false,
|
||||||
|
sort_by: SortBy::Description,
|
||||||
|
},
|
||||||
|
Sort {
|
||||||
|
ascending: true,
|
||||||
|
sort_by: SortBy::Id,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut tasks: Vec<_> = replica.all_tasks().unwrap().values().cloned().collect();
|
||||||
|
sort_tasks(&mut tasks, &report, &working_set);
|
||||||
|
let descriptions: Vec<_> = tasks.iter().map(|t| t.get_description()).collect();
|
||||||
|
assert_eq!(descriptions, vec!["C", "B", "A", "A"]);
|
||||||
|
assert!(tasks[3].has_tag(&("second".try_into().unwrap())));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 task = replica.get_working_set_task(1).unwrap().unwrap();
|
||||||
|
let column = Column {
|
||||||
|
label: s!(""),
|
||||||
|
property: Property::Id,
|
||||||
|
};
|
||||||
|
assert_eq!(task_column(&task, &column, &working_set), s!("1"));
|
||||||
|
|
||||||
|
// get the task that's not in the working set, which should show
|
||||||
|
// a uuid for its id column
|
||||||
|
let task = replica.get_task(&uuids[1]).unwrap().unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
task_column(&task, &column, &working_set),
|
||||||
|
uuids[1].to_string()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn task_column_uuid() {
|
||||||
|
let mut replica = test_replica();
|
||||||
|
create_tasks(&mut replica);
|
||||||
|
let working_set = WorkingSet::new(&mut replica).unwrap();
|
||||||
|
|
||||||
|
let task = replica.get_working_set_task(1).unwrap().unwrap();
|
||||||
|
let column = Column {
|
||||||
|
label: s!(""),
|
||||||
|
property: Property::Uuid,
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
task_column(&task, &column, &working_set),
|
||||||
|
task.get_uuid().to_string()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn task_column_active() {
|
||||||
|
let mut replica = test_replica();
|
||||||
|
let uuids = create_tasks(&mut replica);
|
||||||
|
let working_set = WorkingSet::new(&mut replica).unwrap();
|
||||||
|
|
||||||
|
// make task A active
|
||||||
|
replica
|
||||||
|
.get_task(&uuids[0])
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
.into_mut(&mut replica)
|
||||||
|
.start()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let column = Column {
|
||||||
|
label: s!(""),
|
||||||
|
property: Property::Active,
|
||||||
|
};
|
||||||
|
|
||||||
|
let task = replica.get_working_set_task(1).unwrap().unwrap();
|
||||||
|
assert_eq!(task_column(&task, &column, &working_set), s!("*"));
|
||||||
|
let task = replica.get_working_set_task(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 task = replica.get_working_set_task(2).unwrap().unwrap();
|
||||||
|
let column = Column {
|
||||||
|
label: s!(""),
|
||||||
|
property: Property::Description,
|
||||||
|
};
|
||||||
|
assert_eq!(task_column(&task, &column, &working_set), s!("C"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn task_column_tags() {
|
||||||
|
let mut replica = test_replica();
|
||||||
|
let uuids = create_tasks(&mut replica);
|
||||||
|
let working_set = WorkingSet::new(&mut replica).unwrap();
|
||||||
|
|
||||||
|
// add some tags to task A
|
||||||
|
let mut t1 = replica
|
||||||
|
.get_task(&uuids[0])
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
.into_mut(&mut replica);
|
||||||
|
t1.add_tag(&("foo".try_into().unwrap())).unwrap();
|
||||||
|
t1.add_tag(&("bar".try_into().unwrap())).unwrap();
|
||||||
|
|
||||||
|
let column = Column {
|
||||||
|
label: s!(""),
|
||||||
|
property: Property::Tags,
|
||||||
|
};
|
||||||
|
|
||||||
|
let task = replica.get_working_set_task(1).unwrap().unwrap();
|
||||||
|
assert_eq!(task_column(&task, &column, &working_set), s!("+bar +foo"));
|
||||||
|
let task = replica.get_working_set_task(2).unwrap().unwrap();
|
||||||
|
assert_eq!(task_column(&task, &column, &working_set), s!(""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: test task_column
|
|
@ -148,7 +148,7 @@ pub struct Annotation {
|
||||||
///
|
///
|
||||||
/// This struct contains only getters for various values on the task. The `into_mut` method returns
|
/// This struct contains only getters for various values on the task. The `into_mut` method returns
|
||||||
/// a TaskMut which can be used to modify the task.
|
/// a TaskMut which can be used to modify the task.
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct Task {
|
pub struct Task {
|
||||||
uuid: Uuid,
|
uuid: Uuid,
|
||||||
taskmap: TaskMap,
|
taskmap: TaskMap,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue