support filtering by tags

This commit is contained in:
Dustin J. Mitchell
2020-12-24 16:38:08 +00:00
parent 161c38807d
commit 9c94a7b753
4 changed files with 140 additions and 13 deletions

View File

@@ -1,6 +1,6 @@
use super::args::{arg_matching, id_list, TaskId};
use super::args::{arg_matching, id_list, minus_tag, plus_tag, TaskId};
use super::ArgList;
use nom::{combinator::*, multi::fold_many0, IResult};
use nom::{branch::alt, combinator::*, multi::fold_many0, IResult};
/// A filter represents a selection of a particular set of tasks.
///
@@ -9,8 +9,12 @@ use nom::{combinator::*, multi::fold_many0, IResult};
/// pending tasks, or all tasks.
#[derive(Debug, PartialEq, Default, Clone)]
pub(crate) struct Filter {
/// A list of numeric IDs or prefixes of UUIDs
/// The universe of tasks from which this filter can select
pub(crate) universe: Universe,
/// A set of filter conditions, all of which must match a task in order for that task to be
/// selected.
pub(crate) conditions: Vec<Condition>,
}
/// The universe of tasks over which a filter should be applied.
@@ -39,15 +43,26 @@ impl Default for Universe {
}
}
/// A condition which tasks must match to be accepted by the filter.
#[derive(Debug, PartialEq, Clone)]
pub(crate) enum Condition {
/// Task has the given tag
HasTag(String),
/// Task does not have the given tag
NoTag(String),
}
/// Internal struct representing a parsed filter argument
enum FilterArg {
IdList(Vec<TaskId>),
Condition(Condition),
}
impl Filter {
pub(super) fn parse(input: ArgList) -> IResult<ArgList, Filter> {
fold_many0(
Self::id_list,
alt((Self::id_list, Self::plus_tag, Self::minus_tag)),
Filter {
..Default::default()
},
@@ -68,6 +83,9 @@ impl Filter {
acc.universe = Universe::IdList(id_list);
}
}
FilterArg::Condition(cond) => {
acc.conditions.push(cond);
}
}
acc
}
@@ -78,6 +96,20 @@ impl Filter {
}
map_res(arg_matching(id_list), to_filterarg)(input)
}
fn plus_tag(input: ArgList) -> IResult<ArgList, FilterArg> {
fn to_filterarg(input: &str) -> Result<FilterArg, ()> {
Ok(FilterArg::Condition(Condition::HasTag(input.to_owned())))
}
map_res(arg_matching(plus_tag), to_filterarg)(input)
}
fn minus_tag(input: ArgList) -> IResult<ArgList, FilterArg> {
fn to_filterarg(input: &str) -> Result<FilterArg, ()> {
Ok(FilterArg::Condition(Condition::NoTag(input.to_owned())))
}
map_res(arg_matching(minus_tag), to_filterarg)(input)
}
}
#[cfg(test)]
@@ -159,4 +191,21 @@ mod test {
}
);
}
#[test]
fn test_tags() {
let (input, filter) = Filter::parse(argv!["1", "+yes", "-no"]).unwrap();
assert_eq!(input.len(), 0);
assert_eq!(
filter,
Filter {
universe: Universe::IdList(vec![TaskId::WorkingSetId(1),]),
conditions: vec![
Condition::HasTag("yes".into()),
Condition::NoTag("no".into()),
],
..Default::default()
}
);
}
}

View File

@@ -20,7 +20,7 @@ mod subcommand;
pub(crate) use args::TaskId;
pub(crate) use command::Command;
pub(crate) use filter::{Filter, Universe};
pub(crate) use filter::{Condition, Filter, Universe};
pub(crate) use modification::{DescriptionMod, Modification};
pub(crate) use report::Report;
pub(crate) use subcommand::Subcommand;