189 lines
4.2 KiB
Go
189 lines
4.2 KiB
Go
package search
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"sort"
|
|
"strings"
|
|
"unicode"
|
|
)
|
|
|
|
const (
|
|
KindRepositories = "repositories"
|
|
KindCode = "code"
|
|
KindIssues = "issues"
|
|
KindCommits = "commits"
|
|
)
|
|
|
|
type Query struct {
|
|
Keywords []string
|
|
Kind string
|
|
Limit int
|
|
Order string
|
|
Page int
|
|
Qualifiers Qualifiers
|
|
Sort string
|
|
}
|
|
|
|
type Qualifiers struct {
|
|
Archived *bool
|
|
Assignee string
|
|
Author string
|
|
AuthorDate string
|
|
AuthorEmail string
|
|
AuthorName string
|
|
Base string
|
|
Closed string
|
|
Commenter string
|
|
Comments string
|
|
Committer string
|
|
CommitterDate string
|
|
CommitterEmail string
|
|
CommitterName string
|
|
Created string
|
|
Draft *bool
|
|
Extension string
|
|
Filename string
|
|
Followers string
|
|
Fork string
|
|
Forks string
|
|
GoodFirstIssues string
|
|
Hash string
|
|
Head string
|
|
HelpWantedIssues string
|
|
In []string
|
|
Interactions string
|
|
Involves string
|
|
Is []string
|
|
Label []string
|
|
Language string
|
|
License []string
|
|
Mentions string
|
|
Merge *bool
|
|
Merged string
|
|
Milestone string
|
|
No []string
|
|
Parent string
|
|
Path string
|
|
Project string
|
|
Pushed string
|
|
Reactions string
|
|
Repo []string
|
|
Review string
|
|
ReviewRequested string
|
|
ReviewedBy string
|
|
Size string
|
|
Stars string
|
|
State string
|
|
Status string
|
|
Team string
|
|
TeamReviewRequested string
|
|
Topic []string
|
|
Topics string
|
|
Tree string
|
|
Type string
|
|
Updated string
|
|
User []string
|
|
}
|
|
|
|
func (q Query) String() string {
|
|
qualifiers := formatQualifiers(q.Qualifiers)
|
|
keywords := formatKeywords(q.Keywords)
|
|
all := append(keywords, qualifiers...)
|
|
return strings.TrimSpace(strings.Join(all, " "))
|
|
}
|
|
|
|
func (q Qualifiers) Map() map[string][]string {
|
|
m := map[string][]string{}
|
|
v := reflect.ValueOf(q)
|
|
t := reflect.TypeOf(q)
|
|
for i := 0; i < v.NumField(); i++ {
|
|
fieldName := t.Field(i).Name
|
|
key := camelToKebab(fieldName)
|
|
typ := v.FieldByName(fieldName).Kind()
|
|
value := v.FieldByName(fieldName)
|
|
switch typ {
|
|
case reflect.Ptr:
|
|
if value.IsNil() {
|
|
continue
|
|
}
|
|
v := reflect.Indirect(value)
|
|
m[key] = []string{fmt.Sprintf("%v", v)}
|
|
case reflect.Slice:
|
|
if value.IsNil() {
|
|
continue
|
|
}
|
|
s := []string{}
|
|
for i := 0; i < value.Len(); i++ {
|
|
if value.Index(i).IsZero() {
|
|
continue
|
|
}
|
|
s = append(s, fmt.Sprintf("%v", value.Index(i)))
|
|
}
|
|
m[key] = s
|
|
default:
|
|
if value.IsZero() {
|
|
continue
|
|
}
|
|
m[key] = []string{fmt.Sprintf("%v", value)}
|
|
}
|
|
}
|
|
return m
|
|
}
|
|
|
|
func quote(s string) string {
|
|
if strings.ContainsAny(s, " \"\t\r\n") {
|
|
return fmt.Sprintf("%q", s)
|
|
}
|
|
return s
|
|
}
|
|
|
|
func formatQualifiers(qs Qualifiers) []string {
|
|
var all []string
|
|
for k, vs := range qs.Map() {
|
|
for _, v := range vs {
|
|
all = append(all, fmt.Sprintf("%s:%s", k, quote(v)))
|
|
}
|
|
}
|
|
sort.Strings(all)
|
|
return all
|
|
}
|
|
|
|
func formatKeywords(ks []string) []string {
|
|
result := make([]string, len(ks))
|
|
for i, k := range ks {
|
|
before, after, found := strings.Cut(k, ":")
|
|
if !found {
|
|
result[i] = quote(k)
|
|
} else {
|
|
result[i] = fmt.Sprintf("%s:%s", before, quote(after))
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
// CamelToKebab returns a copy of the string s that is converted from camel case form to '-' separated form.
|
|
func camelToKebab(s string) string {
|
|
var output []rune
|
|
var segment []rune
|
|
for _, r := range s {
|
|
if !unicode.IsLower(r) && string(r) != "-" && !unicode.IsNumber(r) {
|
|
output = addSegment(output, segment)
|
|
segment = nil
|
|
}
|
|
segment = append(segment, unicode.ToLower(r))
|
|
}
|
|
output = addSegment(output, segment)
|
|
return string(output)
|
|
}
|
|
|
|
func addSegment(inrune, segment []rune) []rune {
|
|
if len(segment) == 0 {
|
|
return inrune
|
|
}
|
|
if len(inrune) != 0 {
|
|
inrune = append(inrune, '-')
|
|
}
|
|
inrune = append(inrune, segment...)
|
|
return inrune
|
|
}
|