Skip to content

Commit

Permalink
Add ability to search by arbitrary metadata
Browse files Browse the repository at this point in the history
Example:

~/git/git-bug/git-bug ls --metadata github-url=https://github.com/author/myproject/issues/42

or

~/git/git-bug/git-bug ls metadata:github-url:https://github.com/author/myproject/issues/42

Fixes the cmdline part of <#567>.
  • Loading branch information
vmiklos committed Feb 19, 2021
1 parent 956f98b commit 6d8e709
Show file tree
Hide file tree
Showing 9 changed files with 86 additions and 6 deletions.
18 changes: 18 additions & 0 deletions cache/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,16 @@ func AuthorFilter(query string) Filter {
}
}

// MetadataFilter return a Filter that match a bug metadata at creation time
func MetadataFilter(pair query.StringPair) Filter {
return func(excerpt *BugExcerpt, resolver resolver) bool {
if value, ok := excerpt.CreateMetadata[pair.Key]; ok {
return value == pair.Value
}
return false
}
}

// LabelFilter return a Filter that match a label
func LabelFilter(label string) Filter {
return func(excerpt *BugExcerpt, resolver resolver) bool {
Expand Down Expand Up @@ -109,6 +119,7 @@ func NoLabelFilter() Filter {
type Matcher struct {
Status []Filter
Author []Filter
Metadata []Filter
Actor []Filter
Participant []Filter
Label []Filter
Expand All @@ -127,6 +138,9 @@ func compileMatcher(filters query.Filters) *Matcher {
for _, value := range filters.Author {
result.Author = append(result.Author, AuthorFilter(value))
}
for _, value := range filters.Metadata {
result.Metadata = append(result.Metadata, MetadataFilter(value))
}
for _, value := range filters.Actor {
result.Actor = append(result.Actor, ActorFilter(value))
}
Expand All @@ -153,6 +167,10 @@ func (f *Matcher) Match(excerpt *BugExcerpt, resolver resolver) bool {
return false
}

if match := f.orMatch(f.Metadata, excerpt, resolver); !match {
return false
}

if match := f.orMatch(f.Participant, excerpt, resolver); !match {
return false
}
Expand Down
13 changes: 13 additions & 0 deletions commands/ls.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
type lsOptions struct {
statusQuery []string
authorQuery []string
metadataQuery []string
participantQuery []string
actorQuery []string
labelQuery []string
Expand Down Expand Up @@ -65,6 +66,8 @@ git bug ls status:open --by creation "foo bar" baz
"Filter by status. Valid values are [open,closed]")
flags.StringSliceVarP(&options.authorQuery, "author", "a", nil,
"Filter by author")
flags.StringSliceVarP(&options.metadataQuery, "metadata", "m", nil,
"Filter by metadata. Example: github-url=URL")
flags.StringSliceVarP(&options.participantQuery, "participant", "p", nil,
"Filter by participant")
flags.StringSliceVarP(&options.actorQuery, "actor", "A", nil,
Expand Down Expand Up @@ -337,6 +340,16 @@ func completeQuery(q *query.Query, opts lsOptions) error {
}

q.Author = append(q.Author, opts.authorQuery...)
for _, str := range opts.metadataQuery {
tokens := strings.Split(str, "=")
if len(tokens) < 2 {
return fmt.Errorf("no \"=\" in key=value metadata markup")
}
var pair query.StringPair
pair.Key = tokens[0]
pair.Value = tokens[1]
q.Metadata = append(q.Metadata, pair)
}
q.Participant = append(q.Participant, opts.participantQuery...)
q.Actor = append(q.Actor, opts.actorQuery...)
q.Label = append(q.Label, opts.labelQuery...)
Expand Down
4 changes: 4 additions & 0 deletions doc/man/git-bug-ls.1
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ You can pass an additional query to filter and order the list. This query can be
\fB\-a\fP, \fB\-\-author\fP=[]
Filter by author

.PP
\fB\-m\fP, \fB\-\-metadata\fP=[]
Filter by metadata. Example: github\-url=URL

.PP
\fB\-p\fP, \fB\-\-participant\fP=[]
Filter by participant
Expand Down
1 change: 1 addition & 0 deletions doc/md/git-bug_ls.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ git bug ls status:open --by creation "foo bar" baz
```
-s, --status strings Filter by status. Valid values are [open,closed]
-a, --author strings Filter by author
-m, --metadata strings Filter by metadata. Example: github-url=URL
-p, --participant strings Filter by participant
-A, --actor strings Filter by actor
-l, --label strings Filter by label
Expand Down
6 changes: 6 additions & 0 deletions misc/bash_completion/git-bug
Original file line number Diff line number Diff line change
Expand Up @@ -851,6 +851,12 @@ _git-bug_ls()
local_nonpersistent_flags+=("--author")
local_nonpersistent_flags+=("--author=")
local_nonpersistent_flags+=("-a")
flags+=("--metadata=")
two_word_flags+=("--metadata")
two_word_flags+=("-m")
local_nonpersistent_flags+=("--metadata")
local_nonpersistent_flags+=("--metadata=")
local_nonpersistent_flags+=("-m")
flags+=("--participant=")
two_word_flags+=("--participant")
two_word_flags+=("-p")
Expand Down
2 changes: 2 additions & 0 deletions misc/powershell_completion/git-bug
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ Register-ArgumentCompleter -Native -CommandName 'git-bug' -ScriptBlock {
[CompletionResult]::new('--status', 'status', [CompletionResultType]::ParameterName, 'Filter by status. Valid values are [open,closed]')
[CompletionResult]::new('-a', 'a', [CompletionResultType]::ParameterName, 'Filter by author')
[CompletionResult]::new('--author', 'author', [CompletionResultType]::ParameterName, 'Filter by author')
[CompletionResult]::new('-m', 'm', [CompletionResultType]::ParameterName, 'Filter by metadata. Example: github-url=URL')
[CompletionResult]::new('--metadata', 'metadata', [CompletionResultType]::ParameterName, 'Filter by metadata. Example: github-url=URL')
[CompletionResult]::new('-p', 'p', [CompletionResultType]::ParameterName, 'Filter by participant')
[CompletionResult]::new('--participant', 'participant', [CompletionResultType]::ParameterName, 'Filter by participant')
[CompletionResult]::new('-A', 'A', [CompletionResultType]::ParameterName, 'Filter by actor')
Expand Down
26 changes: 20 additions & 6 deletions query/lexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,20 @@ type tokenKind int
const (
_ tokenKind = iota
tokenKindKV
tokenKindKVV
tokenKindSearch
)

type token struct {
kind tokenKind

// KV
// KV and KVV
qualifier string
value string

// KVV only
rest string

// Search
term string
}
Expand All @@ -33,6 +37,15 @@ func newTokenKV(qualifier, value string) token {
}
}

func newTokenKVV(qualifier, value, rest string) token {
return token{
kind: tokenKindKVV,
qualifier: qualifier,
value: value,
rest: rest,
}
}

func newTokenSearch(term string) token {
return token{
kind: tokenKindSearch,
Expand All @@ -58,18 +71,19 @@ func tokenize(query string) ([]token, error) {
continue
}

if len(split) != 2 {
return nil, fmt.Errorf("can't tokenize \"%s\"", field)
}

if len(split[0]) == 0 {
return nil, fmt.Errorf("can't tokenize \"%s\": empty qualifier", field)
}
if len(split[1]) == 0 {
return nil, fmt.Errorf("empty value for qualifier \"%s\"", split[0])
}

tokens = append(tokens, newTokenKV(split[0], removeQuote(split[1])))
if len(split) == 2 {
tokens = append(tokens, newTokenKV(split[0], removeQuote(split[1])))
} else {
rest := strings.Join(split[2:], ":")
tokens = append(tokens, newTokenKVV(split[0], removeQuote(split[1]), rest))
}
}
return tokens, nil
}
Expand Down
15 changes: 15 additions & 0 deletions query/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,21 @@ func Parse(query string) (*Query, error) {
default:
return nil, fmt.Errorf("unknown qualifier \"%s\"", t.qualifier)
}

case tokenKindKVV:
switch t.qualifier {
case "metadata":
if len(t.rest) == 0 {
return nil, fmt.Errorf("empty value for qualifier \"metadata:%s\"", t.value)
}
var pair StringPair
pair.Key = t.value
pair.Value = t.rest
q.Metadata = append(q.Metadata, pair)

default:
return nil, fmt.Errorf("unknown qualifier \"%s:%s\"", t.qualifier, t.value)
}
}
}
return q, nil
Expand Down
7 changes: 7 additions & 0 deletions query/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,17 @@ func NewQuery() *Query {

type Search []string

// Used for key-value pairs when filtering based on metadata
type StringPair struct {
Key string
Value string
}

// Filters is a collection of Filter that implement a complex filter
type Filters struct {
Status []bug.Status
Author []string
Metadata []StringPair
Actor []string
Participant []string
Label []string
Expand Down

0 comments on commit 6d8e709

Please sign in to comment.