github.com/gfleury/gobbs@v0.0.0-20200831213239-44ca2b94c1a1/search/search.go (about) 1 package search 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "html" 8 "net" 9 "net/http" 10 "strings" 11 12 "github.com/gfleury/gobbs/common" 13 "github.com/gfleury/gobbs/common/log" 14 15 bitbucketv1 "github.com/gfleury/go-bitbucket-v1" 16 "github.com/spf13/cobra" 17 ) 18 19 var ( 20 noColor *bool 21 limit *int 22 ) 23 24 func init() { 25 noColor = Search.Flags().BoolP("nocolor", "N", false, "Disable color output (getting a working diff)") 26 limit = Search.Flags().IntP("limit", "L", 5, "Limit of result items") 27 } 28 29 // Search is the cmd implementation for Searching Users 30 var Search = &cobra.Command{ 31 Use: "search", 32 Aliases: []string{"s"}, 33 Short: "Search stash for any string", 34 Args: cobra.MinimumNArgs(1), 35 RunE: func(cmd *cobra.Command, args []string) error { 36 var results []bitbucketv1.SearchResult 37 38 queryString := args[0] 39 40 if len(args) > 1 { 41 queryString = strings.Join(args, " ") 42 } 43 44 limits := bitbucketv1.Limits{} 45 46 if *limit >= 25 { 47 limits.Primary = 25 48 limits.Secondary = 25 49 } else { 50 limits.Primary = *limit 51 limits.Secondary = *limit 52 } 53 54 searchQuery := bitbucketv1.SearchQuery{ 55 Query: queryString, 56 Limits: limits, 57 } 58 59 for { 60 var hasNext bool 61 apiClient, cancel, err := common.APIClient(cmd) 62 defer cancel() 63 64 if err != nil { 65 cmd.SilenceUsage = true 66 return err 67 } 68 69 response, err := apiClient.DefaultApi.SearchCode(searchQuery) 70 71 if netError, ok := err.(net.Error); (!ok || (ok && !netError.Timeout())) && 72 !errors.Is(err, context.Canceled) && 73 !errors.Is(err, context.DeadlineExceeded) && 74 response != nil && response.Response != nil && 75 response.Response.StatusCode >= http.StatusMultipleChoices { 76 common.PrintApiError(response.Values) 77 cmd.SilenceUsage = true 78 log.Debugf(err.Error()) 79 return fmt.Errorf("Unable to process request, API Error") 80 } else if err != nil { 81 // Print what we already collect 82 if len(results) > 0 { 83 break 84 } 85 cmd.SilenceUsage = true 86 return err 87 } 88 89 pagedSearchResult, err := bitbucketv1.GetSearchResultResponse(response) 90 if err != nil { 91 cmd.SilenceUsage = true 92 return err 93 } 94 results = append(results, pagedSearchResult) 95 96 hasNext = !pagedSearchResult.Code.IsLastPage 97 if _, ok := response.Values["code"]; !hasNext || !ok { 98 break 99 } 100 101 *limit -= pagedSearchResult.Code.Count 102 103 if *limit >= 25 { 104 searchQuery.Entities.Code.Limit = 25 105 } else if *limit <= 0 { 106 break 107 } else { 108 searchQuery.Entities.Code.Limit = *limit 109 } 110 111 searchQuery.Entities.Code.Start = pagedSearchResult.Code.NextStart 112 } 113 114 less, stdin := common.Pager() 115 116 go func() { 117 for idx := range results { 118 for _, value := range results[idx].Code.Values { 119 fmt.Fprintf(stdin, "// Repository\n") 120 fmt.Fprintf(stdin, "// %s/%s: %s\n", value.Repository.Project.Key, value.Repository.Slug, value.File) 121 for i := range value.HitContexts { 122 for j := range value.HitContexts[i] { 123 fmt.Fprintf(stdin, "\t%d %s\n", value.HitContexts[i][j].Line, unHtml(value.HitContexts[i][j].Text, !*noColor)) 124 } 125 } 126 fmt.Fprintf(stdin, "\n") 127 } 128 } 129 defer stdin.Close() 130 }() 131 132 err := less.Run() 133 return err 134 }, 135 } 136 137 func unHtml(line string, color bool) (ret string) { 138 green := "\033[0;32m" 139 reset := "\033[0m" 140 141 if !color { 142 green = "" 143 reset = "" 144 } 145 146 ret = html.UnescapeString(line) 147 ret = strings.ReplaceAll(ret, "<em>", green) 148 ret = strings.ReplaceAll(ret, "</em>", reset) 149 return ret 150 }