Add qualifier module #10

Merged
jolheiser merged 1 commits from qualify into master 2020-03-06 04:18:38 +00:00
4 changed files with 161 additions and 27 deletions

View File

@ -0,0 +1,83 @@
package qualify
import (
"strings"
)
type Query map[string]Filter
type Filter []string
func (q Query) Query() string {
if query, ok := q["query"]; ok {
return strings.Join(query, " ")
}
return ""
}
func (f Filter) Last() string {
return f[len(f)-1]
}
func Parse(text string, qualifiers ...string) Query {
queries := make(map[string]Filter)
for _, field := range fields(text) {
var key, value string
if k, v, ok := qualified(field, qualifiers...); ok {
key = k
value = trim(v)
} else {
key = "query"
value = trim(field)
}
if queries[key] == nil {
queries[key] = make([]string, 0)
}
queries[key] = append(queries[key], value)
}
return queries
}
func qualified(text string, qualifiers ...string) (string, string, bool) {
if len(qualifiers) == 0 && strings.Contains(text, ":") {
kv := strings.SplitN(text, ":", 2)
return kv[0], kv[1], true
}
for _, qualifier := range qualifiers {
if strings.HasPrefix(text, qualifier+":") {
return text[:len(qualifier)], text[len(qualifier)+1:], true
}
}
return "", "", false
}
func fields(text string) []string {
var f []string
var quotes bool
var last int
for idx, char := range text {
if char == '"' {
quotes = !quotes
continue
}
if char == ' ' {
if quotes {
continue
}
f = append(f, text[last:idx])
last = idx + 1
}
}
f = append(f, strings.TrimSpace(text[last:]))
return f
}
func trim(text string) string {
return strings.TrimPrefix(strings.TrimSuffix(strings.TrimSpace(text), `"`), `"`)
}

View File

@ -0,0 +1,49 @@
package qualify
import (
"fmt"
"os"
"strconv"
"strings"
"testing"
)
func TestMain(m *testing.M) {
os.Exit(m.Run())
}
func TestParse(t *testing.T) {
tt := []struct {
Query string
Result string
Qualifiers []string
}{
{"test", "query->test", nil},
{`"test"`, "query->test", nil},
{"author:jolheiser", "author->jolheiser", nil},
{`"this is a test" author:"jolheiser"`, "query->this is a test,author->jolheiser", nil},
{`"this is" "a test" query`, "query->this is|a test|query", nil},
{`label:"a bug" label:"a" label:"bug"`, "label->a bug|a|bug", nil},
{`author:"jolheiser""`, `author->jolheiser"`, nil},
{`author:""jolheiser""`, `author->"jolheiser"`, nil},
{"test:testing", "query->test:testing", []string{"label"}},
}
for idx, tc := range tt {
t.Run(strconv.Itoa(idx+1), func(t *testing.T) {
parsed := result(Parse(tc.Query, tc.Qualifiers...))
if parsed != tc.Result {
t.Logf("\nwant: %s\n got: %s", tc.Result, parsed)
t.Fail()
}
})
}
}
func result(parsed Query) string {
res := make([]string, 0, len(parsed))
for key, values := range parsed {
res = append(res, fmt.Sprintf("%s->%s", key, strings.Join(values, "|")))
}
return strings.Join(res, ",")
}

View File

@ -2,6 +2,7 @@ package sdk
import (
"code.gitea.io/sdk/gitea"
"gitea.com/jolheiser/sip/modules/qualify"
"strings"
)
@ -87,34 +88,34 @@ func (f *IssueFilter) Match(issue *gitea.Issue) bool {
func NewIssueFilter(query string) *IssueFilter {
filter := &IssueFilter{}
for _, q := range strings.Split(query, " ") {
kv := strings.Split(q, ":")
if len(kv) == 2 {
kv[0] = strings.ToLower(kv[0])
kv[1] = strings.ToLower(kv[1])
switch kv[0] {
case "is":
switch kv[1] {
case "open", "opened":
filter.State = Open
case "close", "closed":
filter.State = Closed
case "merge", "merged":
filter.State = Merged
default:
filter.State = None
}
case "author":
filter.Author = kv[1]
case "label":
filter.Labels = append(filter.Labels, kv[1])
case "milestone":
filter.Milestone = kv[1]
}
continue
filters := qualify.Parse(query, "is", "author", "label", "milestone")
if state, ok := filters["is"]; ok {
switch state.Last() {
case "open", "opened":
filter.State = Open
case "close", "closed":
filter.State = Closed
case "merge", "merged":
filter.State = Merged
default:
filter.State = None
}
filter.Query += " " + q
}
filter.Query = strings.TrimSpace(filter.Query)
if author, ok := filters["author"]; ok {
filter.Author = author.Last()
}
if label, ok := filters["label"]; ok {
filter.Labels = append(filter.Labels, label...)
}
if milestone, ok := filters["milestone"]; ok {
filter.Milestone = milestone.Last()
}
filter.Query = filters.Query()
return filter
}

View File

@ -26,6 +26,7 @@ func TestIssueFilter(t *testing.T) {
{"is:closed milestone:0.1.0", &IssueFilter{State: Closed, Milestone: "0.1.0"}},
{"milestone:v1.0.0 keyword", &IssueFilter{Query: "keyword", Milestone: "v1.0.0"}},
{`label:"a bug" keyword`, &IssueFilter{Query: "keyword", Labels: []string{"a bug"}}},
}
for idx, tc := range tt {