mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-06-26 10:54:26 +02:00
Merge pull request #221 from taskchampion/issue177
Require a filter be specified for modifications
This commit is contained in:
commit
9d78654573
2 changed files with 109 additions and 34 deletions
|
@ -1,8 +1,13 @@
|
|||
use super::args::{arg_matching, id_list, minus_tag, plus_tag, status_colon, TaskId};
|
||||
use super::args::{arg_matching, id_list, literal, minus_tag, plus_tag, status_colon, TaskId};
|
||||
use super::ArgList;
|
||||
use crate::usage;
|
||||
use anyhow::bail;
|
||||
use nom::{branch::alt, combinator::*, multi::fold_many0, IResult};
|
||||
use nom::{
|
||||
branch::alt,
|
||||
combinator::*,
|
||||
multi::{fold_many0, fold_many1},
|
||||
IResult,
|
||||
};
|
||||
use taskchampion::Status;
|
||||
|
||||
/// A filter represents a selection of a particular set of tasks.
|
||||
|
@ -85,7 +90,9 @@ impl Condition {
|
|||
}
|
||||
|
||||
impl Filter {
|
||||
pub(super) fn parse(input: ArgList) -> IResult<ArgList, Filter> {
|
||||
/// Parse a filter that can include an empty set of args (meaning
|
||||
/// all tasks)
|
||||
pub(super) fn parse0(input: ArgList) -> IResult<ArgList, Filter> {
|
||||
fold_many0(
|
||||
Condition::parse,
|
||||
Filter {
|
||||
|
@ -95,6 +102,30 @@ impl Filter {
|
|||
)(input)
|
||||
}
|
||||
|
||||
/// Parse a filter that must have at least one arg, which can be `all`
|
||||
/// to mean all tasks
|
||||
pub(super) fn parse1(input: ArgList) -> IResult<ArgList, Filter> {
|
||||
alt((
|
||||
Filter::parse_all,
|
||||
fold_many1(
|
||||
Condition::parse,
|
||||
Filter {
|
||||
..Default::default()
|
||||
},
|
||||
|acc, arg| acc.with_arg(arg),
|
||||
),
|
||||
))(input)
|
||||
}
|
||||
|
||||
fn parse_all(input: ArgList) -> IResult<ArgList, Filter> {
|
||||
fn to_filter(_: &str) -> Result<Filter, ()> {
|
||||
Ok(Filter {
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
map_res(arg_matching(literal("all")), to_filter)(input)
|
||||
}
|
||||
|
||||
/// fold multiple filter args into a single Filter instance
|
||||
fn with_arg(mut self, cond: Condition) -> Filter {
|
||||
if let Condition::IdList(mut id_list) = cond {
|
||||
|
@ -157,6 +188,13 @@ impl Filter {
|
|||
description: "
|
||||
Select tasks with the given status.",
|
||||
});
|
||||
u.filters.push(usage::Filter {
|
||||
syntax: "all",
|
||||
summary: "All tasks",
|
||||
description: "
|
||||
When specified alone for task-modification commands, `all` matches all tasks.
|
||||
For example, `task all done` will mark all tasks as done.",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,8 +203,8 @@ mod test {
|
|||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_empty() {
|
||||
let (input, filter) = Filter::parse(argv![]).unwrap();
|
||||
fn test_empty_parse0() {
|
||||
let (input, filter) = Filter::parse0(argv![]).unwrap();
|
||||
assert_eq!(input.len(), 0);
|
||||
assert_eq!(
|
||||
filter,
|
||||
|
@ -176,9 +214,46 @@ mod test {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_parse1() {
|
||||
// parse1 does not allow empty input
|
||||
assert!(Filter::parse1(argv![]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_all_parse0() {
|
||||
let (input, _) = Filter::parse0(argv!["all"]).unwrap();
|
||||
assert_eq!(input.len(), 1); // did not parse "all"
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_all_parse1() {
|
||||
let (input, filter) = Filter::parse1(argv!["all"]).unwrap();
|
||||
assert_eq!(input.len(), 0);
|
||||
assert_eq!(
|
||||
filter,
|
||||
Filter {
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_all_with_other_stuff() {
|
||||
let (input, filter) = Filter::parse1(argv!["all", "+foo"]).unwrap();
|
||||
// filter ends after `all`
|
||||
assert_eq!(input.len(), 1);
|
||||
assert_eq!(
|
||||
filter,
|
||||
Filter {
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_id_list_single() {
|
||||
let (input, filter) = Filter::parse(argv!["1"]).unwrap();
|
||||
let (input, filter) = Filter::parse0(argv!["1"]).unwrap();
|
||||
assert_eq!(input.len(), 0);
|
||||
assert_eq!(
|
||||
filter,
|
||||
|
@ -190,7 +265,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_id_list_commas() {
|
||||
let (input, filter) = Filter::parse(argv!["1,2,3"]).unwrap();
|
||||
let (input, filter) = Filter::parse0(argv!["1,2,3"]).unwrap();
|
||||
assert_eq!(input.len(), 0);
|
||||
assert_eq!(
|
||||
filter,
|
||||
|
@ -206,7 +281,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_id_list_multi_arg() {
|
||||
let (input, filter) = Filter::parse(argv!["1,2", "3,4"]).unwrap();
|
||||
let (input, filter) = Filter::parse0(argv!["1,2", "3,4"]).unwrap();
|
||||
assert_eq!(input.len(), 0);
|
||||
assert_eq!(
|
||||
filter,
|
||||
|
@ -223,7 +298,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_id_list_uuids() {
|
||||
let (input, filter) = Filter::parse(argv!["1,abcd1234"]).unwrap();
|
||||
let (input, filter) = Filter::parse0(argv!["1,abcd1234"]).unwrap();
|
||||
assert_eq!(input.len(), 0);
|
||||
assert_eq!(
|
||||
filter,
|
||||
|
@ -238,7 +313,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_tags() {
|
||||
let (input, filter) = Filter::parse(argv!["1", "+yes", "-no"]).unwrap();
|
||||
let (input, filter) = Filter::parse0(argv!["1", "+yes", "-no"]).unwrap();
|
||||
assert_eq!(input.len(), 0);
|
||||
assert_eq!(
|
||||
filter,
|
||||
|
@ -254,7 +329,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_status() {
|
||||
let (input, filter) = Filter::parse(argv!["status:completed", "status:pending"]).unwrap();
|
||||
let (input, filter) = Filter::parse0(argv!["status:completed", "status:pending"]).unwrap();
|
||||
assert_eq!(input.len(), 0);
|
||||
assert_eq!(
|
||||
filter,
|
||||
|
@ -269,8 +344,8 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn intersect_idlist_idlist() {
|
||||
let left = Filter::parse(argv!["1,2", "+yes"]).unwrap().1;
|
||||
let right = Filter::parse(argv!["2,3", "+no"]).unwrap().1;
|
||||
let left = Filter::parse0(argv!["1,2", "+yes"]).unwrap().1;
|
||||
let right = Filter::parse0(argv!["2,3", "+no"]).unwrap().1;
|
||||
let both = left.intersect(right);
|
||||
assert_eq!(
|
||||
both,
|
||||
|
@ -289,8 +364,8 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn intersect_idlist_alltasks() {
|
||||
let left = Filter::parse(argv!["1,2", "+yes"]).unwrap().1;
|
||||
let right = Filter::parse(argv!["+no"]).unwrap().1;
|
||||
let left = Filter::parse0(argv!["1,2", "+yes"]).unwrap().1;
|
||||
let right = Filter::parse0(argv!["+no"]).unwrap().1;
|
||||
let both = left.intersect(right);
|
||||
assert_eq!(
|
||||
both,
|
||||
|
@ -308,8 +383,8 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn intersect_alltasks_alltasks() {
|
||||
let left = Filter::parse(argv!["+yes"]).unwrap().1;
|
||||
let right = Filter::parse(argv!["+no"]).unwrap().1;
|
||||
let left = Filter::parse0(argv!["+yes"]).unwrap().1;
|
||||
let right = Filter::parse0(argv!["+no"]).unwrap().1;
|
||||
let both = left.intersect(right);
|
||||
assert_eq!(
|
||||
both,
|
||||
|
|
|
@ -217,7 +217,7 @@ impl Modify {
|
|||
}
|
||||
map_res(
|
||||
tuple((
|
||||
Filter::parse,
|
||||
Filter::parse1,
|
||||
alt((
|
||||
arg_matching(literal("modify")),
|
||||
arg_matching(literal("prepend")),
|
||||
|
@ -235,47 +235,47 @@ impl Modify {
|
|||
fn get_usage(u: &mut usage::Usage) {
|
||||
u.subcommands.push(usage::Subcommand {
|
||||
name: "modify",
|
||||
syntax: "[filter] modify [modification]",
|
||||
syntax: "<filter> modify [modification]",
|
||||
summary: "Modify tasks",
|
||||
description: "
|
||||
Modify all tasks matching the filter.",
|
||||
Modify all tasks matching the required filter.",
|
||||
});
|
||||
u.subcommands.push(usage::Subcommand {
|
||||
name: "prepend",
|
||||
syntax: "[filter] prepend [modification]",
|
||||
syntax: "<filter> prepend [modification]",
|
||||
summary: "Prepend task description",
|
||||
description: "
|
||||
Modify all tasks matching the filter by inserting the given description before each
|
||||
Modify all tasks matching the required filter by inserting the given description before each
|
||||
task's description.",
|
||||
});
|
||||
u.subcommands.push(usage::Subcommand {
|
||||
name: "append",
|
||||
syntax: "[filter] append [modification]",
|
||||
syntax: "<filter> append [modification]",
|
||||
summary: "Append task description",
|
||||
description: "
|
||||
Modify all tasks matching the filter by adding the given description to the end
|
||||
Modify all tasks matching the required filter by adding the given description to the end
|
||||
of each task's description.",
|
||||
});
|
||||
u.subcommands.push(usage::Subcommand {
|
||||
name: "start",
|
||||
syntax: "[filter] start [modification]",
|
||||
syntax: "<filter> start [modification]",
|
||||
summary: "Start tasks",
|
||||
description: "
|
||||
Start all tasks matching the filter, additionally applying any given modifications."
|
||||
Start all tasks matching the required filter, additionally applying any given modifications."
|
||||
});
|
||||
u.subcommands.push(usage::Subcommand {
|
||||
name: "stop",
|
||||
syntax: "[filter] stop [modification]",
|
||||
syntax: "<filter> stop [modification]",
|
||||
summary: "Stop tasks",
|
||||
description: "
|
||||
Stop all tasks matching the filter, additionally applying any given modifications.",
|
||||
Stop all tasks matching the required filter, additionally applying any given modifications.",
|
||||
});
|
||||
u.subcommands.push(usage::Subcommand {
|
||||
name: "done",
|
||||
syntax: "[filter] done [modification]",
|
||||
syntax: "<filter> done [modification]",
|
||||
summary: "Mark tasks as completed",
|
||||
description: "
|
||||
Mark all tasks matching the filter as completed, additionally applying any given
|
||||
Mark all tasks matching the required filter as completed, additionally applying any given
|
||||
modifications.",
|
||||
});
|
||||
}
|
||||
|
@ -293,14 +293,14 @@ impl Report {
|
|||
}
|
||||
// allow the filter expression before or after the report name
|
||||
alt((
|
||||
map_res(pair(arg_matching(report_name), Filter::parse), |input| {
|
||||
map_res(pair(arg_matching(report_name), Filter::parse0), |input| {
|
||||
to_subcommand(input.1, input.0)
|
||||
}),
|
||||
map_res(pair(Filter::parse, arg_matching(report_name)), |input| {
|
||||
map_res(pair(Filter::parse0, arg_matching(report_name)), |input| {
|
||||
to_subcommand(input.0, input.1)
|
||||
}),
|
||||
// default to a "next" report
|
||||
map_res(Filter::parse, |input| to_subcommand(input, "next")),
|
||||
map_res(Filter::parse0, |input| to_subcommand(input, "next")),
|
||||
))(input)
|
||||
}
|
||||
|
||||
|
@ -335,7 +335,7 @@ impl Info {
|
|||
}
|
||||
map_res(
|
||||
pair(
|
||||
Filter::parse,
|
||||
Filter::parse1,
|
||||
alt((
|
||||
arg_matching(literal("info")),
|
||||
arg_matching(literal("debug")),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue