Compare commits
6 commits
a3077ca313
...
61232b70fc
| Author | SHA1 | Date | |
|---|---|---|---|
| 61232b70fc | |||
| d46f5e010e | |||
| 674329dfee | |||
| 6c9243fcc6 | |||
| d8d4e8ece8 | |||
| a40fa602eb |
2 changed files with 76 additions and 96 deletions
|
|
@ -24,31 +24,22 @@ struct Args {
|
|||
enum Commands {
|
||||
/// List issues.
|
||||
List {
|
||||
/// Filter strings, describes issues to include in the list.
|
||||
/// Each filter string is of the form "name=condition".
|
||||
/// The supported names and their matching conditions are:
|
||||
/// Filter string, describes issues to include in the list.
|
||||
/// The filter string is composed of chunks separated by ":".
|
||||
/// Each chunk is of the form "name=condition". The supported
|
||||
/// names and their matching conditions are:
|
||||
///
|
||||
/// "state": Comma-separated list of states to list.
|
||||
/// Example: "state=new,backlog". Defaults to
|
||||
/// "new,backlog,blocked,inprogress".
|
||||
///
|
||||
/// "assignee": Comma-separated list of assignees to include in
|
||||
/// the list. The empty string includes issues with no assignee.
|
||||
/// Example: "assignee=seb," lists issues assigned to "seb" and
|
||||
/// issues without an assignee. Defaults to include all issues.
|
||||
/// "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 "-". Example: "tag=bug,-docs" shows issues
|
||||
/// that are tagged "bug" and not tagged "docs". Defaults to
|
||||
/// including all tags and excluding none.
|
||||
/// "tag": Comma-separated list of tags to include or exclude
|
||||
/// (if prefixed with "-"). If omitted, 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<String>,
|
||||
#[arg(default_value_t = String::from("state=New,Backlog,Blocked,InProgress"))]
|
||||
filter: String,
|
||||
},
|
||||
|
||||
/// Create a new issue.
|
||||
|
|
@ -94,7 +85,7 @@ enum Commands {
|
|||
tag: Option<String>,
|
||||
},
|
||||
|
||||
/// Get or set the `done_time` of the Issue.
|
||||
// Set the `done_time` of the Issue.
|
||||
DoneTime {
|
||||
issue_id: String,
|
||||
done_time: Option<String>,
|
||||
|
|
@ -108,14 +99,7 @@ fn handle_command(
|
|||
match &args.command {
|
||||
Commands::List { filter } => {
|
||||
let issues = entomologist::database::read_issues_database(issues_database_source)?;
|
||||
let filter = {
|
||||
let mut f = entomologist::Filter::new();
|
||||
for filter_str in filter {
|
||||
f.parse(filter_str)?;
|
||||
}
|
||||
f
|
||||
};
|
||||
|
||||
let filter = entomologist::Filter::new_from_str(filter)?;
|
||||
let mut uuids_by_state = std::collections::HashMap::<
|
||||
entomologist::issue::State,
|
||||
Vec<&entomologist::issue::IssueHandle>,
|
||||
|
|
|
|||
42
src/lib.rs
42
src/lib.rs
|
|
@ -6,8 +6,6 @@ pub mod git;
|
|||
pub mod issue;
|
||||
pub mod issues;
|
||||
|
||||
use crate::issue::State;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ParseFilterError {
|
||||
#[error("Failed to parse filter")]
|
||||
|
|
@ -32,8 +30,9 @@ pub struct Filter<'a> {
|
|||
}
|
||||
|
||||
impl<'a> Filter<'a> {
|
||||
pub fn new() -> Filter<'a> {
|
||||
Self {
|
||||
pub fn new_from_str(filter_str: &'a str) -> Result<Filter<'a>, ParseFilterError> {
|
||||
use crate::issue::State;
|
||||
let mut f = Filter {
|
||||
include_states: std::collections::HashSet::<crate::issue::State>::from([
|
||||
State::InProgress,
|
||||
State::Blocked,
|
||||
|
|
@ -45,61 +44,57 @@ impl<'a> Filter<'a> {
|
|||
exclude_tags: std::collections::HashSet::<&'a str>::new(),
|
||||
start_done_time: None,
|
||||
end_done_time: None,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub fn parse(&mut self, filter_str: &'a str) -> Result<(), ParseFilterError> {
|
||||
let tokens: Vec<&str> = filter_str.split("=").collect();
|
||||
for filter_chunk_str in filter_str.split(" ") {
|
||||
let tokens: Vec<&str> = filter_chunk_str.split("=").collect();
|
||||
if tokens.len() != 2 {
|
||||
return Err(ParseFilterError::ParseError);
|
||||
}
|
||||
|
||||
match tokens[0] {
|
||||
"state" => {
|
||||
self.include_states.clear();
|
||||
f.include_states.clear();
|
||||
for s in tokens[1].split(",") {
|
||||
self.include_states
|
||||
.insert(crate::issue::State::from_str(s)?);
|
||||
f.include_states.insert(crate::issue::State::from_str(s)?);
|
||||
}
|
||||
}
|
||||
|
||||
"assignee" => {
|
||||
self.include_assignees.clear();
|
||||
f.include_assignees.clear();
|
||||
for s in tokens[1].split(",") {
|
||||
self.include_assignees.insert(s);
|
||||
f.include_assignees.insert(s);
|
||||
}
|
||||
}
|
||||
|
||||
"tag" => {
|
||||
self.include_tags.clear();
|
||||
self.exclude_tags.clear();
|
||||
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() == '-' {
|
||||
self.exclude_tags.insert(&s[1..]);
|
||||
f.exclude_tags.insert(&s[1..]);
|
||||
} else {
|
||||
self.include_tags.insert(s);
|
||||
f.include_tags.insert(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"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(
|
||||
f.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(
|
||||
f.end_done_time = Some(
|
||||
chrono::DateTime::parse_from_rfc3339(times[1])?
|
||||
.with_timezone(&chrono::Local),
|
||||
);
|
||||
|
|
@ -107,11 +102,12 @@ impl<'a> Filter<'a> {
|
|||
}
|
||||
|
||||
_ => {
|
||||
println!("unknown filter string '{}'", filter_str);
|
||||
println!("unknown filter chunk '{}'", filter_chunk_str);
|
||||
return Err(ParseFilterError::ParseError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(f)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue