github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/pkg/search/query.go (about) 1 package search 2 3 import ( 4 "fmt" 5 "reflect" 6 "sort" 7 "strings" 8 "unicode" 9 ) 10 11 const ( 12 KindRepositories = "repositories" 13 KindIssues = "issues" 14 ) 15 16 type Query struct { 17 Keywords []string 18 Kind string 19 Limit int 20 Order string 21 Page int 22 Qualifiers Qualifiers 23 Sort string 24 } 25 26 type Qualifiers struct { 27 Archived *bool 28 Assignee string 29 Author string 30 Base string 31 Closed string 32 Commenter string 33 Comments string 34 Created string 35 Draft *bool 36 Followers string 37 Fork string 38 Forks string 39 GoodFirstIssues string 40 Head string 41 HelpWantedIssues string 42 In []string 43 Interactions string 44 Involves string 45 Is []string 46 Label []string 47 Language string 48 License []string 49 Mentions string 50 Merged string 51 Milestone string 52 No []string 53 Project string 54 Pushed string 55 Reactions string 56 Repo []string 57 Review string 58 ReviewRequested string 59 ReviewedBy string 60 Size string 61 Stars string 62 State string 63 Status string 64 Team string 65 TeamReviewRequested string 66 Topic []string 67 Topics string 68 Type string 69 Updated string 70 User string 71 } 72 73 func (q Query) String() string { 74 qualifiers := formatQualifiers(q.Qualifiers) 75 keywords := formatKeywords(q.Keywords) 76 all := append(keywords, qualifiers...) 77 return strings.TrimSpace(strings.Join(all, " ")) 78 } 79 80 func (q Qualifiers) Map() map[string][]string { 81 m := map[string][]string{} 82 v := reflect.ValueOf(q) 83 t := reflect.TypeOf(q) 84 for i := 0; i < v.NumField(); i++ { 85 fieldName := t.Field(i).Name 86 key := camelToKebab(fieldName) 87 typ := v.FieldByName(fieldName).Kind() 88 value := v.FieldByName(fieldName) 89 switch typ { 90 case reflect.Ptr: 91 if value.IsNil() { 92 continue 93 } 94 v := reflect.Indirect(value) 95 m[key] = []string{fmt.Sprintf("%v", v)} 96 case reflect.Slice: 97 if value.IsNil() { 98 continue 99 } 100 s := []string{} 101 for i := 0; i < value.Len(); i++ { 102 if value.Index(i).IsZero() { 103 continue 104 } 105 s = append(s, fmt.Sprintf("%v", value.Index(i))) 106 } 107 m[key] = s 108 default: 109 if value.IsZero() { 110 continue 111 } 112 m[key] = []string{fmt.Sprintf("%v", value)} 113 } 114 } 115 return m 116 } 117 118 func quote(s string) string { 119 if strings.ContainsAny(s, " \"\t\r\n") { 120 return fmt.Sprintf("%q", s) 121 } 122 return s 123 } 124 125 func formatQualifiers(qs Qualifiers) []string { 126 var all []string 127 for k, vs := range qs.Map() { 128 for _, v := range vs { 129 all = append(all, fmt.Sprintf("%s:%s", k, quote(v))) 130 } 131 } 132 sort.Strings(all) 133 return all 134 } 135 136 func formatKeywords(ks []string) []string { 137 for i, k := range ks { 138 ks[i] = quote(k) 139 } 140 return ks 141 } 142 143 // CamelToKebab returns a copy of the string s that is converted from camel case form to '-' separated form. 144 func camelToKebab(s string) string { 145 var output []rune 146 var segment []rune 147 for _, r := range s { 148 if !unicode.IsLower(r) && string(r) != "-" && !unicode.IsNumber(r) { 149 output = addSegment(output, segment) 150 segment = nil 151 } 152 segment = append(segment, unicode.ToLower(r)) 153 } 154 output = addSegment(output, segment) 155 return string(output) 156 } 157 158 func addSegment(inrune, segment []rune) []rune { 159 if len(segment) == 0 { 160 return inrune 161 } 162 if len(inrune) != 0 { 163 inrune = append(inrune, '-') 164 } 165 inrune = append(inrune, segment...) 166 return inrune 167 }