mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-07-07 20:06:36 +02:00
Support CLI syntax for changing tags
This commit is contained in:
parent
28c5fb2268
commit
54e8484bc2
4 changed files with 97 additions and 35 deletions
|
@ -58,7 +58,6 @@ pub(super) fn id_list(input: &str) -> IResult<&str, Vec<&str>> {
|
|||
}
|
||||
|
||||
/// Recognizes a tag prefixed with `+` and returns the tag value
|
||||
#[allow(dead_code)] // tags not implemented yet
|
||||
pub(super) fn plus_tag(input: &str) -> IResult<&str, &str> {
|
||||
fn to_tag(input: (char, &str)) -> Result<&str, ()> {
|
||||
Ok(input.1)
|
||||
|
@ -70,7 +69,6 @@ pub(super) fn plus_tag(input: &str) -> IResult<&str, &str> {
|
|||
}
|
||||
|
||||
/// Recognizes a tag prefixed with `-` and returns the tag value
|
||||
#[allow(dead_code)] // tags not implemented yet
|
||||
pub(super) fn minus_tag(input: &str) -> IResult<&str, &str> {
|
||||
fn to_tag(input: (char, &str)) -> Result<&str, ()> {
|
||||
Ok(input.1)
|
||||
|
@ -81,18 +79,6 @@ pub(super) fn minus_tag(input: &str) -> IResult<&str, &str> {
|
|||
)(input)
|
||||
}
|
||||
|
||||
/// Recognizes a tag prefixed with either `-` or `+`, returning true for + and false for -
|
||||
#[allow(dead_code)] // tags not implemented yet
|
||||
pub(super) fn tag(input: &str) -> IResult<&str, (bool, &str)> {
|
||||
fn to_plus(input: &str) -> Result<(bool, &str), ()> {
|
||||
Ok((true, input))
|
||||
}
|
||||
fn to_minus(input: &str) -> Result<(bool, &str), ()> {
|
||||
Ok((false, input))
|
||||
}
|
||||
alt((map_res(plus_tag, to_plus), map_res(minus_tag, to_minus)))(input)
|
||||
}
|
||||
|
||||
/// Consume a single argument from an argument list that matches the given string parser (one
|
||||
/// of the other functions in this module). The given parser must consume the entire input.
|
||||
pub(super) fn arg_matching<'a, O, F>(f: F) -> impl Fn(ArgList<'a>) -> IResult<ArgList, O>
|
||||
|
@ -131,14 +117,10 @@ mod test {
|
|||
#[test]
|
||||
fn test_arg_matching() {
|
||||
assert_eq!(
|
||||
arg_matching(tag)(argv!["+foo", "bar"]).unwrap(),
|
||||
(argv!["bar"], (true, "foo"))
|
||||
arg_matching(plus_tag)(argv!["+foo", "bar"]).unwrap(),
|
||||
(argv!["bar"], "foo")
|
||||
);
|
||||
assert_eq!(
|
||||
arg_matching(tag)(argv!["-foo", "bar"]).unwrap(),
|
||||
(argv!["bar"], (false, "foo"))
|
||||
);
|
||||
assert!(arg_matching(tag)(argv!["foo", "bar"]).is_err());
|
||||
assert!(arg_matching(plus_tag)(argv!["foo", "bar"]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -161,16 +143,6 @@ mod test {
|
|||
assert!(minus_tag("-1abc").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tag() {
|
||||
assert_eq!(tag("-abc").unwrap().1, (false, "abc"));
|
||||
assert_eq!(tag("+abc123").unwrap().1, (true, "abc123"));
|
||||
assert!(tag("+abc123 --").is_err());
|
||||
assert!(tag("-abc123 ").is_err());
|
||||
assert!(tag(" -abc123").is_err());
|
||||
assert!(tag("-1abc").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_literal() {
|
||||
assert_eq!(literal("list")("list").unwrap().1, "list");
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use super::args::{any, arg_matching};
|
||||
use super::args::{any, arg_matching, minus_tag, plus_tag};
|
||||
use super::ArgList;
|
||||
use nom::{combinator::*, multi::fold_many0, IResult};
|
||||
use nom::{branch::alt, combinator::*, multi::fold_many0, IResult};
|
||||
use std::collections::HashSet;
|
||||
use taskchampion::Status;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
|
@ -34,13 +35,21 @@ pub struct Modification {
|
|||
/// Set the status
|
||||
pub status: Option<Status>,
|
||||
|
||||
/// Set the "active" status, that is, start (true) or stop (false) the task.
|
||||
/// Set the "active" state, that is, start (true) or stop (false) the task.
|
||||
pub active: Option<bool>,
|
||||
|
||||
/// Add tags
|
||||
pub add_tags: HashSet<String>,
|
||||
|
||||
/// Remove tags
|
||||
pub remove_tags: HashSet<String>,
|
||||
}
|
||||
|
||||
/// A single argument that is part of a modification, used internally to this module
|
||||
enum ModArg<'a> {
|
||||
Description(&'a str),
|
||||
PlusTag(&'a str),
|
||||
MinusTag(&'a str),
|
||||
}
|
||||
|
||||
impl Modification {
|
||||
|
@ -55,11 +64,22 @@ impl Modification {
|
|||
acc.description = DescriptionMod::Set(description.to_string());
|
||||
}
|
||||
}
|
||||
ModArg::PlusTag(tag) => {
|
||||
acc.add_tags.insert(tag.to_owned());
|
||||
}
|
||||
ModArg::MinusTag(tag) => {
|
||||
acc.remove_tags.insert(tag.to_owned());
|
||||
}
|
||||
}
|
||||
acc
|
||||
}
|
||||
fold_many0(
|
||||
Self::description,
|
||||
alt((
|
||||
Self::plus_tag,
|
||||
Self::minus_tag,
|
||||
// this must come last
|
||||
Self::description,
|
||||
)),
|
||||
Modification {
|
||||
..Default::default()
|
||||
},
|
||||
|
@ -73,6 +93,20 @@ impl Modification {
|
|||
}
|
||||
map_res(arg_matching(any), to_modarg)(input)
|
||||
}
|
||||
|
||||
fn plus_tag(input: ArgList) -> IResult<ArgList, ModArg> {
|
||||
fn to_modarg(input: &str) -> Result<ModArg, ()> {
|
||||
Ok(ModArg::PlusTag(input))
|
||||
}
|
||||
map_res(arg_matching(plus_tag), to_modarg)(input)
|
||||
}
|
||||
|
||||
fn minus_tag(input: ArgList) -> IResult<ArgList, ModArg> {
|
||||
fn to_modarg(input: &str) -> Result<ModArg, ()> {
|
||||
Ok(ModArg::MinusTag(input))
|
||||
}
|
||||
map_res(arg_matching(minus_tag), to_modarg)(input)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -104,6 +138,19 @@ mod test {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_tags() {
|
||||
let (input, modification) = Modification::parse(argv!["+abc", "+def"]).unwrap();
|
||||
assert_eq!(input.len(), 0);
|
||||
assert_eq!(
|
||||
modification,
|
||||
Modification {
|
||||
add_tags: set!["abc".to_owned(), "def".to_owned()],
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multi_arg_description() {
|
||||
let (input, modification) = Modification::parse(argv!["new", "desc", "fun"]).unwrap();
|
||||
|
@ -116,4 +163,20 @@ mod test {
|
|||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multi_arg_description_and_tags() {
|
||||
let (input, modification) =
|
||||
Modification::parse(argv!["new", "+next", "desc", "-daytime", "fun"]).unwrap();
|
||||
assert_eq!(input.len(), 0);
|
||||
assert_eq!(
|
||||
modification,
|
||||
Modification {
|
||||
description: DescriptionMod::Set("new desc fun".to_owned()),
|
||||
add_tags: set!["next".to_owned()],
|
||||
remove_tags: set!["daytime".to_owned()],
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::argparse::{DescriptionMod, Modification};
|
||||
use failure::Fallible;
|
||||
use std::convert::TryInto;
|
||||
use taskchampion::TaskMut;
|
||||
use termcolor::WriteColor;
|
||||
|
||||
|
@ -32,6 +33,18 @@ pub(super) fn apply_modification<W: WriteColor>(
|
|||
task.stop()?;
|
||||
}
|
||||
|
||||
for tag in modification.add_tags.iter() {
|
||||
// note that the parser should have already ensured that this tag was valid
|
||||
let tag = tag.try_into()?;
|
||||
task.add_tag(&tag)?;
|
||||
}
|
||||
|
||||
for tag in modification.remove_tags.iter() {
|
||||
// note that the parser should have already ensured that this tag was valid
|
||||
let tag = tag.try_into()?;
|
||||
task.remove_tag(&tag)?;
|
||||
}
|
||||
|
||||
write!(w, "modified task {}\n", task.get_uuid())?;
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -10,3 +10,17 @@ macro_rules! argv {
|
|||
&[$($x),*][..]
|
||||
);
|
||||
}
|
||||
|
||||
/// Create a hashset, similar to vec!
|
||||
#[cfg(test)]
|
||||
macro_rules! set(
|
||||
{ $($key:expr),+ } => {
|
||||
{
|
||||
let mut s = ::std::collections::HashSet::new();
|
||||
$(
|
||||
s.insert($key);
|
||||
)+
|
||||
s
|
||||
}
|
||||
};
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue