mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-08-21 07:43:08 +02:00
Implement modifying tasks' "wait" value
This commit is contained in:
parent
d7d703f135
commit
e977fb294c
8 changed files with 166 additions and 4 deletions
|
@ -22,6 +22,8 @@ termcolor = "^1.1.2"
|
|||
atty = "^0.2.14"
|
||||
toml = "^0.5.8"
|
||||
toml_edit = "^0.2.0"
|
||||
chrono = "*"
|
||||
lazy_static = "1"
|
||||
|
||||
# only needed for usage-docs
|
||||
mdbook = { version = "0.4", optional = true }
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
//! Parsers for argument lists -- arrays of strings
|
||||
use super::ArgList;
|
||||
use super::NOW;
|
||||
use chrono::prelude::*;
|
||||
use nom::bytes::complete::tag as nomtag;
|
||||
use nom::{
|
||||
branch::*,
|
||||
|
@ -67,6 +69,30 @@ pub(super) fn status_colon(input: &str) -> IResult<&str, Status> {
|
|||
map_res(colon_prefixed("status"), to_status)(input)
|
||||
}
|
||||
|
||||
/// Recognizes timestamps
|
||||
pub(super) fn timestamp(input: &str) -> IResult<&str, DateTime<Utc>> {
|
||||
// TODO: full relative date language supported by TW
|
||||
fn nn_d_to_timestamp(input: &str) -> Result<DateTime<Utc>, ()> {
|
||||
// TODO: don't unwrap
|
||||
Ok(*NOW + chrono::Duration::days(input.parse().unwrap()))
|
||||
}
|
||||
map_res(terminated(digit1, char('d')), nn_d_to_timestamp)(input)
|
||||
}
|
||||
|
||||
/// Recognizes `wait:` to None and `wait:<ts>` to `Some(ts)`
|
||||
pub(super) fn wait_colon(input: &str) -> IResult<&str, Option<DateTime<Utc>>> {
|
||||
fn to_wait(input: DateTime<Utc>) -> Result<Option<DateTime<Utc>>, ()> {
|
||||
Ok(Some(input))
|
||||
}
|
||||
fn to_none(_: &str) -> Result<Option<DateTime<Utc>>, ()> {
|
||||
Ok(None)
|
||||
}
|
||||
preceded(
|
||||
nomtag("wait:"),
|
||||
alt((map_res(timestamp, to_wait), map_res(nomtag(""), to_none))),
|
||||
)(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> {
|
||||
|
@ -237,6 +263,17 @@ mod test {
|
|||
assert!(minus_tag("-1abc").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wait() {
|
||||
assert_eq!(wait_colon("wait:").unwrap(), ("", None));
|
||||
|
||||
let one_day = *NOW + chrono::Duration::days(1);
|
||||
assert_eq!(wait_colon("wait:1d").unwrap(), ("", Some(one_day)));
|
||||
|
||||
let one_day = *NOW + chrono::Duration::days(1);
|
||||
assert_eq!(wait_colon("wait:1d2").unwrap(), ("2", Some(one_day)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_literal() {
|
||||
assert_eq!(literal("list")("list").unwrap().1, "list");
|
||||
|
|
|
@ -31,9 +31,16 @@ pub(crate) use modification::{DescriptionMod, Modification};
|
|||
pub(crate) use subcommand::Subcommand;
|
||||
|
||||
use crate::usage::Usage;
|
||||
use chrono::prelude::*;
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
type ArgList<'a> = &'a [&'a str];
|
||||
|
||||
lazy_static! {
|
||||
// A static value of NOW to make tests easier
|
||||
pub(super) static ref NOW: DateTime<Utc> = Utc::now();
|
||||
}
|
||||
|
||||
pub(crate) fn get_usage(usage: &mut Usage) {
|
||||
Subcommand::get_usage(usage);
|
||||
Filter::get_usage(usage);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use super::args::{any, arg_matching, minus_tag, plus_tag};
|
||||
use super::args::{any, arg_matching, minus_tag, plus_tag, wait_colon};
|
||||
use super::ArgList;
|
||||
use crate::usage;
|
||||
use chrono::prelude::*;
|
||||
use nom::{branch::alt, combinator::*, multi::fold_many0, IResult};
|
||||
use std::collections::HashSet;
|
||||
use taskchampion::Status;
|
||||
|
@ -36,6 +37,9 @@ pub struct Modification {
|
|||
/// Set the status
|
||||
pub status: Option<Status>,
|
||||
|
||||
/// Set (or, with `Some(None)`, clear) the wait timestamp
|
||||
pub wait: Option<Option<DateTime<Utc>>>,
|
||||
|
||||
/// Set the "active" state, that is, start (true) or stop (false) the task.
|
||||
pub active: Option<bool>,
|
||||
|
||||
|
@ -51,6 +55,7 @@ enum ModArg<'a> {
|
|||
Description(&'a str),
|
||||
PlusTag(&'a str),
|
||||
MinusTag(&'a str),
|
||||
Wait(Option<DateTime<Utc>>),
|
||||
}
|
||||
|
||||
impl Modification {
|
||||
|
@ -71,6 +76,9 @@ impl Modification {
|
|||
ModArg::MinusTag(tag) => {
|
||||
acc.remove_tags.insert(tag.to_owned());
|
||||
}
|
||||
ModArg::Wait(wait) => {
|
||||
acc.wait = Some(wait);
|
||||
}
|
||||
}
|
||||
acc
|
||||
}
|
||||
|
@ -78,6 +86,7 @@ impl Modification {
|
|||
alt((
|
||||
Self::plus_tag,
|
||||
Self::minus_tag,
|
||||
Self::wait,
|
||||
// this must come last
|
||||
Self::description,
|
||||
)),
|
||||
|
@ -109,6 +118,13 @@ impl Modification {
|
|||
map_res(arg_matching(minus_tag), to_modarg)(input)
|
||||
}
|
||||
|
||||
fn wait(input: ArgList) -> IResult<ArgList, ModArg> {
|
||||
fn to_modarg(input: Option<DateTime<Utc>>) -> Result<ModArg<'static>, ()> {
|
||||
Ok(ModArg::Wait(input))
|
||||
}
|
||||
map_res(arg_matching(wait_colon), to_modarg)(input)
|
||||
}
|
||||
|
||||
pub(super) fn get_usage(u: &mut usage::Usage) {
|
||||
u.modifications.push(usage::Modification {
|
||||
syntax: "DESCRIPTION",
|
||||
|
@ -122,14 +138,25 @@ impl Modification {
|
|||
u.modifications.push(usage::Modification {
|
||||
syntax: "+TAG",
|
||||
summary: "Tag task",
|
||||
description: "
|
||||
Add the given tag to the task.",
|
||||
description: "Add the given tag to the task.",
|
||||
});
|
||||
u.modifications.push(usage::Modification {
|
||||
syntax: "-TAG",
|
||||
summary: "Un-tag task",
|
||||
description: "Remove the given tag from the task.",
|
||||
});
|
||||
u.modifications.push(usage::Modification {
|
||||
syntax: "status:{pending,completed,deleted}",
|
||||
summary: "Set the task's status",
|
||||
description: "Set the status of the task explicitly.",
|
||||
});
|
||||
u.modifications.push(usage::Modification {
|
||||
syntax: "wait:<timestamp>",
|
||||
summary: "Set or unset the task's wait time",
|
||||
description: "
|
||||
Remove the given tag from the task.",
|
||||
Set the time before which the task is not actionable and
|
||||
should not be shown in reports. With `wait:`, the time
|
||||
is un-set.",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -137,6 +164,7 @@ impl Modification {
|
|||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::argparse::NOW;
|
||||
|
||||
#[test]
|
||||
fn test_empty() {
|
||||
|
@ -176,6 +204,32 @@ mod test {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_wait() {
|
||||
let (input, modification) = Modification::parse(argv!["wait:2d"]).unwrap();
|
||||
assert_eq!(input.len(), 0);
|
||||
assert_eq!(
|
||||
modification,
|
||||
Modification {
|
||||
wait: Some(Some(*NOW + chrono::Duration::days(2))),
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unset_wait() {
|
||||
let (input, modification) = Modification::parse(argv!["wait:"]).unwrap();
|
||||
assert_eq!(input.len(), 0);
|
||||
assert_eq!(
|
||||
modification,
|
||||
Modification {
|
||||
wait: Some(None),
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multi_arg_description() {
|
||||
let (input, modification) = Modification::parse(argv!["new", "desc", "fun"]).unwrap();
|
||||
|
|
|
@ -40,5 +40,9 @@ pub(super) fn apply_modification(
|
|||
task.remove_tag(&tag)?;
|
||||
}
|
||||
|
||||
if let Some(wait) = modification.wait {
|
||||
task.set_wait(wait)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue