[enh] add context specific search functionality

pull/94/head
Adam Tauber 7 years ago
parent f652335a16
commit 82d1ae6412

@ -119,7 +119,13 @@ var COMMANDS map[string]func(string, *App) CommandFunc = map[string]func(string,
return func(g *gocui.Gui, v *gocui.View) error {
return openEditor(g, v, a.config.General.Editor)
}
},
"toggleContextSpecificSearch": func(_ string, a *App) CommandFunc {
return func(g *gocui.Gui, v *gocui.View) error {
a.config.General.ContextSpecificSearch = !a.config.General.ContextSpecificSearch
a.PrintBody(g)
return nil
}
},
}

@ -35,6 +35,7 @@ type Config struct {
}
type GeneralOptions struct {
ContextSpecificSearch bool
DefaultURLScheme string
Editor string
FollowRedirects bool
@ -58,6 +59,7 @@ var DefaultKeys = map[string]map[string]string{
"CtrlD": "deleteLine",
"CtrlW": "deleteWord",
"CtrlO": "openEditor",
"CtrlT": "toggleContextSpecificSearch",
"Tab": "nextView",
"CtrlJ": "nextView",
"CtrlK": "prevView",
@ -102,7 +104,7 @@ var DefaultConfig = Config{
FormatJSON: true,
Insecure: false,
PreserveScrollPosition: true,
StatusLine: "[wuzz {{.Version}}]{{if .Duration}} [Response time: {{.Duration}}]{{end}}",
StatusLine: "[wuzz {{.Version}}]{{if .Duration}} [Response time: {{.Duration}}]{{end}}] [Request no.: {{.RequestNumber}}/{{.HistorySize}}] [Search type: {{.SearchType}}]",
Timeout: Duration{
defaultTimeoutDuration,
},

@ -2,6 +2,7 @@ package formatter
import (
"encoding/hex"
"errors"
"fmt"
"io"
)
@ -21,3 +22,7 @@ func (f *binaryFormatter) Title() string {
func (f *binaryFormatter) Searchable() bool {
return false
}
func (f *binaryFormatter) Search(q string, body []byte) ([]string, error) {
return nil, errors.New("Cannot perform search on binary content type")
}

@ -12,6 +12,7 @@ type ResponseFormatter interface {
Format(writer io.Writer, data []byte) error
Title() string
Searchable() bool
Search(string, []byte) ([]string, error)
}
func New(appConfig *config.Config, contentType string) ResponseFormatter {
@ -23,6 +24,6 @@ func New(appConfig *config.Config, contentType string) ResponseFormatter {
} else if strings.Index(contentType, "text") == -1 && strings.Index(contentType, "application") == -1 {
return &binaryFormatter{}
} else {
return &textFormatter{}
return &TextFormatter{}
}
}

@ -9,7 +9,7 @@ import (
)
type htmlFormatter struct {
textFormatter
TextFormatter
}
func (f *htmlFormatter) Format(writer io.Writer, data []byte) error {

@ -5,11 +5,13 @@ import (
"errors"
"io"
"github.com/tidwall/gjson"
"github.com/nwidger/jsoncolor"
)
type jsonFormatter struct {
textFormatter
parsedBody gjson.Result
TextFormatter
}
func (f *jsonFormatter) Format(writer io.Writer, data []byte) error {
@ -26,3 +28,26 @@ func (f *jsonFormatter) Format(writer io.Writer, data []byte) error {
func (f *jsonFormatter) Title() string {
return "[json]"
}
func (f *jsonFormatter) Search(q string, body []byte) ([]string, error) {
if q != "" {
if f.parsedBody.Type != gjson.JSON {
f.parsedBody = gjson.ParseBytes(body)
}
searchResult := f.parsedBody.Get(q)
if searchResult.Type == gjson.Null {
return nil, errors.New("Invalid gjson query or no results found")
}
if searchResult.Type != gjson.JSON {
return []string{searchResult.String()}, nil
}
body = []byte(searchResult.String())
}
jsonFormatter := jsoncolor.NewFormatter()
buf := bytes.NewBuffer(make([]byte, 0, len(body)))
err := jsonFormatter.Format(buf, body)
if err != nil {
return nil, errors.New("Invalid results")
}
return []string{string(buf.Bytes())}, nil
}

@ -2,20 +2,33 @@ package formatter
import (
"io"
"regexp"
)
type textFormatter struct {
type TextFormatter struct {
}
func (f *textFormatter) Format(writer io.Writer, data []byte) error {
func (f *TextFormatter) Format(writer io.Writer, data []byte) error {
_, err := writer.Write(data)
return err
}
func (f *textFormatter) Title() string {
func (f *TextFormatter) Title() string {
return "[text]"
}
func (f *textFormatter) Searchable() bool {
func (f *TextFormatter) Searchable() bool {
return true
}
func (f *TextFormatter) Search(q string, body []byte) ([]string, error) {
search_re, err := regexp.Compile(q)
if err != nil {
return nil, err
}
ret := make([]string, 0, 16)
for _, match := range search_re.FindAll(body, 1000) {
ret = append(ret, string(match))
}
return ret, nil
}

@ -16,6 +16,7 @@ CtrlS = "saveResponse"
CtrlD = "deleteLine"
CtrlW = "deleteWord"
CtrlE = "saveRequest"
CtrlT = "toggleContextSpecificSearch"
Tab = "nextView"
CtrlJ = "nextView"
CtrlK = "prevView"

@ -39,6 +39,16 @@ func (s *StatusLineFunctions) RequestNumber() string {
return strconv.Itoa(i)
}
func (s *StatusLineFunctions) SearchType() string {
if len(s.app.history) > 0 && !s.app.history[s.app.historyIndex].Formatter.Searchable() {
return "none"
}
if s.app.config.General.ContextSpecificSearch {
return "response specific"
}
return "regex"
}
func (s *StatusLine) Update(v *gocui.View, a *App) {
v.Clear()
err := s.tpl.Execute(v, &StatusLineFunctions{app: a})

@ -268,6 +268,8 @@ var METHODS = []string{
const DEFAULT_METHOD = http.MethodGet
var DEFAULT_FORMATTER = &formatter.TextFormatter{}
var CLIENT = &http.Client{
Timeout: time.Duration(TIMEOUT_DURATION * time.Second),
}
@ -310,6 +312,7 @@ type Request struct {
RawResponseBody []byte
ContentType string
Duration time.Duration
Formatter formatter.ResponseFormatter
}
type App struct {
@ -914,7 +917,13 @@ func (a *App) PrintBody(g *gocui.Gui) {
vrb, _ := g.View(RESPONSE_BODY_VIEW)
vrb.Clear()
responseFormatter := formatter.New(a.config, req.ContentType)
var responseFormatter formatter.ResponseFormatter
if req.Formatter == nil {
req.Formatter = formatter.New(a.config, req.ContentType)
}
responseFormatter = req.Formatter
vrb.Title = VIEW_PROPERTIES[vrb.Name()].title + " " + responseFormatter.Title()
search_text := getViewValue(g, "search")
@ -929,13 +938,15 @@ func (a *App) PrintBody(g *gocui.Gui) {
}
return nil
}
if !a.config.General.ContextSpecificSearch {
responseFormatter = DEFAULT_FORMATTER
}
vrb.SetOrigin(0, 0)
search_re, err := regexp.Compile(search_text)
results, err := responseFormatter.Search(search_text, req.RawResponseBody)
if err != nil {
fmt.Fprint(vrb, "Error: invalid search regexp")
fmt.Fprint(vrb, "Search error: ", err)
return nil
}
results := search_re.FindAll(req.RawResponseBody, 1000)
if len(results) == 0 {
vrb.Title = "No results"
fmt.Fprint(vrb, "Error: no results")

Loading…
Cancel
Save