ent list: add filtering based on tags

This commit is contained in:
Sebastian Kuzminsky 2025-07-12 14:24:40 -06:00
parent 28db7669f4
commit 9d4409c008
3 changed files with 50 additions and 0 deletions

View file

@ -34,6 +34,10 @@ enum Commands {
/// "assignee": Comma-separated list of assignees to list. /// "assignee": Comma-separated list of assignees to list.
/// Defaults to all assignees if not set. /// 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"))] #[arg(default_value_t = String::from("state=New,Backlog,Blocked,InProgress"))]
filter: String, 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. // This issue passed all the filters, include it in list.
uuids_by_state uuids_by_state
.entry(issue.state.clone()) .entry(issue.state.clone())

View file

@ -347,6 +347,20 @@ impl Issue {
))?; ))?;
Ok(()) 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. // This is the internal/private API of Issue.

View file

@ -20,6 +20,8 @@ pub enum ParseFilterError {
pub struct Filter<'a> { pub struct Filter<'a> {
pub include_states: std::collections::HashSet<crate::issue::State>, pub include_states: std::collections::HashSet<crate::issue::State>,
pub include_assignees: std::collections::HashSet<&'a str>, 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> { impl<'a> Filter<'a> {
@ -33,6 +35,8 @@ impl<'a> Filter<'a> {
State::New, State::New,
]), ]),
include_assignees: std::collections::HashSet::<&'a str>::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(":") { 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)?); f.include_states.insert(crate::issue::State::from_str(s)?);
} }
} }
"assignee" => { "assignee" => {
f.include_assignees.clear(); f.include_assignees.clear();
for s in tokens[1].split(",") { for s in tokens[1].split(",") {
f.include_assignees.insert(s); 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); println!("unknown filter chunk '{}'", filter_chunk_str);
return Err(ParseFilterError::ParseError); return Err(ParseFilterError::ParseError);