diff --git a/src/bin/ent/main.rs b/src/bin/ent/main.rs index ac55045..a597fd9 100644 --- a/src/bin/ent/main.rs +++ b/src/bin/ent/main.rs @@ -41,6 +41,13 @@ enum Commands { /// if prefixed with "-". Example: "tag=bug,-docs" shows issues /// that are tagged "bug" and not tagged "docs". Defaults to /// including all tags and excluding none. + /// + /// "done-time": Time range of issue completion, in the form + /// "[START]..[END]". Includes issues that were marked Done + /// between START and END. START and END are both in RFC 3339 + /// format, e.g. "YYYY-MM-DDTHH:MM:SS[+-]HH:MM". If START + /// is omitted, defaults to the beginning of time. If END is + /// omitted, defaults to the end of time. filter: Vec, }, @@ -138,6 +145,19 @@ fn handle_command( } } + if let Some(issue_done_time) = issue.done_time { + if let Some(start_done_time) = filter.start_done_time { + if start_done_time > issue_done_time { + continue; + } + } + if let Some(end_done_time) = filter.end_done_time { + if end_done_time < issue_done_time { + continue; + } + } + } + // This issue passed all the filters, include it in list. uuids_by_state .entry(issue.state.clone()) diff --git a/src/lib.rs b/src/lib.rs index dbb19e5..b6245b9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,6 +14,8 @@ pub enum ParseFilterError { ParseError, #[error(transparent)] IssueParseError(#[from] crate::issue::IssueError), + #[error(transparent)] + ChronoParseError(#[from] chrono::format::ParseError), } // FIXME: It's easy to imagine a full dsl for filtering issues, for now @@ -25,6 +27,8 @@ pub struct Filter<'a> { pub include_assignees: std::collections::HashSet<&'a str>, pub include_tags: std::collections::HashSet<&'a str>, pub exclude_tags: std::collections::HashSet<&'a str>, + pub start_done_time: Option>, + pub end_done_time: Option>, } impl<'a> Filter<'a> { @@ -39,6 +43,8 @@ impl<'a> Filter<'a> { include_assignees: std::collections::HashSet::<&'a str>::new(), include_tags: std::collections::HashSet::<&'a str>::new(), exclude_tags: std::collections::HashSet::<&'a str>::new(), + start_done_time: None, + end_done_time: None, } } @@ -79,6 +85,27 @@ impl<'a> Filter<'a> { } } + "done-time" => { + self.start_done_time = None; + self.end_done_time = None; + let times: Vec<&str> = tokens[1].split("..").collect(); + if times.len() > 2 { + return Err(ParseFilterError::ParseError); + } + if times[0].len() != 0 { + self.start_done_time = Some( + chrono::DateTime::parse_from_rfc3339(times[0])? + .with_timezone(&chrono::Local), + ); + } + if times[1].len() != 0 { + self.end_done_time = Some( + chrono::DateTime::parse_from_rfc3339(times[1])? + .with_timezone(&chrono::Local), + ); + } + } + _ => { println!("unknown filter string '{}'", filter_str); return Err(ParseFilterError::ParseError);