diff --git a/src/bin/ent/main.rs b/src/bin/ent/main.rs index e09b1a9..d67ef3c 100644 --- a/src/bin/ent/main.rs +++ b/src/bin/ent/main.rs @@ -34,6 +34,10 @@ enum Commands { /// "assignee": Comma-separated list of assignees to list. /// Defaults to all assignees if not set. /// + /// "tag": Comma-separated list of tags to include or exclude + /// (if prefixed with "-"). If omitted, defaults to including + /// all tags and excluding none. + /// #[arg(default_value_t = String::from("state=New,Backlog,Blocked,InProgress"))] filter: String, }, @@ -187,6 +191,17 @@ fn handle_command( } } + if filter.include_tags.len() > 0 { + if !issue.has_any_tag(&filter.include_tags) { + continue; + } + } + if filter.exclude_tags.len() > 0 { + if issue.has_any_tag(&filter.exclude_tags) { + continue; + } + } + // This issue passed all the filters, include it in list. uuids_by_state .entry(issue.state.clone()) diff --git a/src/issue.rs b/src/issue.rs index 8cdbd4f..bd0632c 100644 --- a/src/issue.rs +++ b/src/issue.rs @@ -347,6 +347,20 @@ impl Issue { ))?; Ok(()) } + + pub fn has_tag(&self, tag: &str) -> bool { + let tag_string = String::from(tag); + self.tags.iter().position(|x| x == &tag_string).is_some() + } + + pub fn has_any_tag(&self, tags: &std::collections::HashSet<&str>) -> bool { + for tag in tags.iter() { + if self.has_tag(tag) { + return true; + } + } + return false; + } } // This is the internal/private API of Issue. diff --git a/src/lib.rs b/src/lib.rs index b28fb74..fa820b4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,6 +20,8 @@ pub enum ParseFilterError { pub struct Filter<'a> { pub include_states: std::collections::HashSet, pub include_assignees: std::collections::HashSet<&'a str>, + pub include_tags: std::collections::HashSet<&'a str>, + pub exclude_tags: std::collections::HashSet<&'a str>, } impl<'a> Filter<'a> { @@ -33,6 +35,8 @@ impl<'a> Filter<'a> { State::New, ]), include_assignees: std::collections::HashSet::<&'a str>::new(), + include_tags: std::collections::HashSet::<&'a str>::new(), + exclude_tags: std::collections::HashSet::<&'a str>::new(), }; for filter_chunk_str in filter_str.split(":") { @@ -48,12 +52,29 @@ impl<'a> Filter<'a> { f.include_states.insert(crate::issue::State::from_str(s)?); } } + "assignee" => { f.include_assignees.clear(); for s in tokens[1].split(",") { f.include_assignees.insert(s); } } + + "tag" => { + f.include_tags.clear(); + f.exclude_tags.clear(); + for s in tokens[1].split(",") { + if s.len() == 0 { + return Err(ParseFilterError::ParseError); + } + if s.chars().nth(0).unwrap() == '-' { + f.exclude_tags.insert(&s[1..]); + } else { + f.include_tags.insert(s); + } + } + } + _ => { println!("unknown filter chunk '{}'", filter_chunk_str); return Err(ParseFilterError::ParseError);