use super::{any, id_list, timestamp, TaskId}; use crate::argparse::NOW; use nom::bytes::complete::tag as nomtag; use nom::{branch::*, character::complete::*, combinator::*, sequence::*, IResult}; use taskchampion::chrono::prelude::*; use taskchampion::Status; /// Recognizes up to the colon of the common `:...` syntax fn colon_prefix(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(crate) fn status_colon(input: &str) -> IResult<&str, Status> { fn to_status(input: &str) -> Result { match input { "pending" => Ok(Status::Pending), "completed" => Ok(Status::Completed), "deleted" => Ok(Status::Deleted), _ => Err(()), } } map_res(colon_prefix("status"), to_status)(input) } /// Recognizes `wait:` to None and `wait:` to `Some(ts)` pub(crate) fn wait_colon(input: &str) -> IResult<&str, Option>> { fn to_wait(input: DateTime) -> Result>, ()> { Ok(Some(input)) } fn to_none(_: &str) -> Result>, ()> { Ok(None) } preceded( nomtag("wait:"), alt(( map_res(timestamp(*NOW, Local), to_wait), map_res(nomtag(""), to_none), )), )(input) } /// Recognizes `depends:` to `(true, )` and `depends:-` to `(false, )`. pub(crate) fn depends_colon(input: &str) -> IResult<&str, (bool, Vec)> { fn to_bool(maybe_minus: Option) -> Result { Ok(maybe_minus.is_none()) // None -> true, Some -> false } preceded( nomtag("depends:"), pair(map_res(opt(char('-')), to_bool), id_list), )(input) } #[cfg(test)] mod test { use super::*; use pretty_assertions::assert_eq; use taskchampion::chrono::Duration; #[test] fn test_colon_prefix() { assert_eq!(colon_prefix("foo")("foo:abc").unwrap().1, "abc"); assert_eq!(colon_prefix("foo")("foo:").unwrap().1, ""); assert!(colon_prefix("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_wait() { assert_eq!(wait_colon("wait:").unwrap(), ("", None)); let one_day = *NOW + Duration::days(1); assert_eq!(wait_colon("wait:1d").unwrap(), ("", Some(one_day))); let one_day = *NOW + Duration::days(1); assert_eq!(wait_colon("wait:1d2").unwrap(), ("2", Some(one_day))); } }