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  }