mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-06-26 10:54:26 +02:00
Allow filtering by status
This commit is contained in:
parent
83d8fc3b4e
commit
b7c12eec1e
2 changed files with 91 additions and 13 deletions
|
@ -10,7 +10,7 @@ use nom::{
|
|||
sequence::*,
|
||||
Err, IResult,
|
||||
};
|
||||
use taskchampion::Uuid;
|
||||
use taskchampion::{Status, Uuid};
|
||||
|
||||
/// A task identifier, as given in a filter command-line expression
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
|
@ -40,6 +40,32 @@ pub(super) fn literal(literal: &'static str) -> impl Fn(&str) -> IResult<&str, &
|
|||
move |input: &str| all_consuming(nomtag(literal))(input)
|
||||
}
|
||||
|
||||
/// Recognizes a colon-prefixed pair
|
||||
pub(super) fn colon_prefixed(prefix: &'static str) -> impl Fn(&str) -> IResult<&str, &str> {
|
||||
fn to_suffix<'a>(input: (&'a str, char, &'a str)) -> Result<&'a str, ()> {
|
||||
Ok(input.2)
|
||||
}
|
||||
move |input: &str| {
|
||||
map_res(
|
||||
all_consuming(tuple((nomtag(prefix), char(':'), any))),
|
||||
to_suffix,
|
||||
)(input)
|
||||
}
|
||||
}
|
||||
|
||||
/// Recognizes `status:{pending,completed,deleted}`
|
||||
pub(super) fn status_colon(input: &str) -> IResult<&str, Status> {
|
||||
fn to_status(input: &str) -> Result<Status, ()> {
|
||||
match input {
|
||||
"pending" => Ok(Status::Pending),
|
||||
"completed" => Ok(Status::Completed),
|
||||
"deleted" => Ok(Status::Deleted),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
map_res(colon_prefixed("status"), to_status)(input)
|
||||
}
|
||||
|
||||
/// Recognizes a comma-separated list of TaskIds
|
||||
pub(super) fn id_list(input: &str) -> IResult<&str, Vec<TaskId>> {
|
||||
fn hex_n(n: usize) -> impl Fn(&str) -> IResult<&str, &str> {
|
||||
|
@ -164,6 +190,26 @@ mod test {
|
|||
assert!(arg_matching(plus_tag)(argv!["foo", "bar"]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_colon_prefixed() {
|
||||
assert_eq!(colon_prefixed("foo")("foo:abc").unwrap().1, "abc");
|
||||
assert_eq!(colon_prefixed("foo")("foo:").unwrap().1, "");
|
||||
assert!(colon_prefixed("foo")("foo").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_status_colon() {
|
||||
assert_eq!(status_colon("status:pending").unwrap().1, Status::Pending);
|
||||
assert_eq!(
|
||||
status_colon("status:completed").unwrap().1,
|
||||
Status::Completed
|
||||
);
|
||||
assert_eq!(status_colon("status:deleted").unwrap().1, Status::Deleted);
|
||||
assert!(status_colon("status:foo").is_err());
|
||||
assert!(status_colon("status:complete").is_err());
|
||||
assert!(status_colon("status").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_plus_tag() {
|
||||
assert_eq!(plus_tag("+abc").unwrap().1, "abc");
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use super::args::{arg_matching, id_list, minus_tag, plus_tag, TaskId};
|
||||
use super::args::{arg_matching, id_list, minus_tag, plus_tag, status_colon, TaskId};
|
||||
use super::ArgList;
|
||||
use crate::usage;
|
||||
use nom::{branch::alt, combinator::*, multi::fold_many0, IResult};
|
||||
|
@ -25,7 +25,6 @@ pub(crate) enum Condition {
|
|||
/// Task does not have the given tag
|
||||
NoTag(String),
|
||||
|
||||
// TODO: add command-line syntax for this
|
||||
/// Task has the given status
|
||||
Status(Status),
|
||||
|
||||
|
@ -36,7 +35,12 @@ pub(crate) enum Condition {
|
|||
impl Filter {
|
||||
pub(super) fn parse(input: ArgList) -> IResult<ArgList, Filter> {
|
||||
fold_many0(
|
||||
alt((Self::id_list, Self::plus_tag, Self::minus_tag)),
|
||||
alt((
|
||||
Self::parse_id_list,
|
||||
Self::parse_plus_tag,
|
||||
Self::parse_minus_tag,
|
||||
Self::parse_status,
|
||||
)),
|
||||
Filter {
|
||||
..Default::default()
|
||||
},
|
||||
|
@ -78,25 +82,32 @@ impl Filter {
|
|||
|
||||
// parsers
|
||||
|
||||
fn id_list(input: ArgList) -> IResult<ArgList, Condition> {
|
||||
fn to_filterarg(input: Vec<TaskId>) -> Result<Condition, ()> {
|
||||
fn parse_id_list(input: ArgList) -> IResult<ArgList, Condition> {
|
||||
fn to_condition(input: Vec<TaskId>) -> Result<Condition, ()> {
|
||||
Ok(Condition::IdList(input))
|
||||
}
|
||||
map_res(arg_matching(id_list), to_filterarg)(input)
|
||||
map_res(arg_matching(id_list), to_condition)(input)
|
||||
}
|
||||
|
||||
fn plus_tag(input: ArgList) -> IResult<ArgList, Condition> {
|
||||
fn to_filterarg(input: &str) -> Result<Condition, ()> {
|
||||
fn parse_plus_tag(input: ArgList) -> IResult<ArgList, Condition> {
|
||||
fn to_condition(input: &str) -> Result<Condition, ()> {
|
||||
Ok(Condition::HasTag(input.to_owned()))
|
||||
}
|
||||
map_res(arg_matching(plus_tag), to_filterarg)(input)
|
||||
map_res(arg_matching(plus_tag), to_condition)(input)
|
||||
}
|
||||
|
||||
fn minus_tag(input: ArgList) -> IResult<ArgList, Condition> {
|
||||
fn to_filterarg(input: &str) -> Result<Condition, ()> {
|
||||
fn parse_minus_tag(input: ArgList) -> IResult<ArgList, Condition> {
|
||||
fn to_condition(input: &str) -> Result<Condition, ()> {
|
||||
Ok(Condition::NoTag(input.to_owned()))
|
||||
}
|
||||
map_res(arg_matching(minus_tag), to_filterarg)(input)
|
||||
map_res(arg_matching(minus_tag), to_condition)(input)
|
||||
}
|
||||
|
||||
fn parse_status(input: ArgList) -> IResult<ArgList, Condition> {
|
||||
fn to_condition(input: Status) -> Result<Condition, ()> {
|
||||
Ok(Condition::Status(input))
|
||||
}
|
||||
map_res(arg_matching(status_colon), to_condition)(input)
|
||||
}
|
||||
|
||||
// usage
|
||||
|
@ -123,6 +134,12 @@ impl Filter {
|
|||
description: "
|
||||
Select tasks that do not have the given tag.",
|
||||
});
|
||||
u.filters.push(usage::Filter {
|
||||
syntax: "status:pending, status:completed, status:deleted",
|
||||
summary: "Task status",
|
||||
description: "
|
||||
Select tasks with the given status.",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -218,6 +235,21 @@ mod test {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_status() {
|
||||
let (input, filter) = Filter::parse(argv!["status:completed", "status:pending"]).unwrap();
|
||||
assert_eq!(input.len(), 0);
|
||||
assert_eq!(
|
||||
filter,
|
||||
Filter {
|
||||
conditions: vec![
|
||||
Condition::Status(Status::Completed),
|
||||
Condition::Status(Status::Pending),
|
||||
],
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn intersect_idlist_idlist() {
|
||||
let left = Filter::parse(argv!["1,2", "+yes"]).unwrap().1;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue