Skip to content

Commit

Permalink
structured substitution
Browse files Browse the repository at this point in the history
  • Loading branch information
hbagdi committed Feb 20, 2022
1 parent fce89e6 commit f19539f
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 39 deletions.
2 changes: 0 additions & 2 deletions .golangci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ linters:
- nilnil
- noctx
- nolintlint
- paralleltest
- prealloc
- predeclared
- promlinter
Expand All @@ -57,7 +56,6 @@ linters:
- stylecheck
- tagliatelle
- tenv
- testpackage
- thelper
- tparallel
- typecheck
Expand Down
11 changes: 9 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@ module github.com/hbagdi/hit

go 1.17

require github.com/ghodss/yaml v1.0.0
require (
github.com/ghodss/yaml v1.0.0
github.com/tidwall/gjson v1.14.0
)

require gopkg.in/yaml.v2 v2.2.8 // indirect
require (
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
gopkg.in/yaml.v2 v2.2.8 // indirect
)
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/tidwall/gjson v1.14.0 h1:6aeJ0bzojgWLa82gDQHcx3S0Lr/O51I9bJ5nv6JFx5w=
github.com/tidwall/gjson v1.14.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
Expand Down
114 changes: 79 additions & 35 deletions pkg/request/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ import (
"net/url"
"os"
"path"
"regexp"
"strconv"
"strings"

"github.com/ghodss/yaml"
"github.com/hbagdi/hit/pkg/cache"
"github.com/hbagdi/hit/pkg/parser"
"github.com/tidwall/gjson"
)

const (
Expand All @@ -36,7 +36,6 @@ func Generate(global parser.Global,
var body []byte
switch request.BodyEncoding {
case encodingY2J:

var i interface{}
err := yaml.Unmarshal([]byte(bodyS), &i)
if err != nil {
Expand All @@ -51,7 +50,7 @@ func Generate(global parser.Global,
if err != nil {
return nil, err
}
body, err = deRef(bodyS, func(key string) ([]byte, error) {
fn := func(key string) (interface{}, error) {
key = key[1:]
n, err := strconv.Atoi(key)
if err == nil && n < len(os.Args) {
Expand All @@ -73,20 +72,19 @@ func Generate(global parser.Global,
return nil, fmt.Errorf("key not found: %v", key)
}
}
return []byte(fmt.Sprintf("%v", r)), nil
})
if err != nil {
return nil, err
return r, nil
}
var i interface{}
err = yaml.Unmarshal(body, &i)

jsonBytes, err := yaml.YAMLToJSON([]byte(bodyS))
if err != nil {
return nil, err
}
body, err = json.Marshal(i)
r := &Resolver{Fn: fn}
body, err = r.Resolve(jsonBytes)
if err != nil {
return nil, err
}

case "":
default:
return nil, fmt.Errorf("invalid encoding: %v", request.BodyEncoding)
Expand All @@ -104,33 +102,79 @@ func Generate(global parser.Global,
return httpReq, nil
}

func deRef(input string, m func(string) ([]byte, error)) ([]byte, error) {
var res []byte
j := 0
i := 0
for i < len(input) {
if input[i] != '@' {
i++
continue
}
// '@' found, copy until '@'
res = append(res, input[j:i]...)
key := keyName(input[i:])
r, err := m(key)
if err != nil {
return nil, err
}
res = append(res, r...)
type SubFn func(string) (interface{}, error)

i += len(key)
j = i
}
res = append(res, input[j:]...)
return res, nil
type Resolver struct {
Fn SubFn
res interface{}
err error
}

var nameRE = regexp.MustCompile("^@[a-zA-Z0-9-.]+")
func (r *Resolver) Resolve(input []byte) ([]byte, error) {
g := gjson.ParseBytes(input)
r.res, r.err = r.deRefJSON(g)
if r.err != nil {
return nil, r.err
}
return json.Marshal(r.res)
}

func keyName(input string) string {
return nameRE.FindString(input)
func (r *Resolver) deRefJSON(j gjson.Result) (interface{}, error) {
if j.IsArray() {
var res []interface{}
var iteratorErr error
j.ForEach(func(key, value gjson.Result) bool {
r, err := r.deRefJSON(value)
if err != nil {
iteratorErr = err
return false
}
res = append(res, r)
return true
})
if iteratorErr != nil {
return nil, iteratorErr
}
return res, nil
}
if j.IsObject() {
res := map[string]interface{}{}
var iteratorErr error
j.ForEach(func(key, value gjson.Result) bool {
r, err := r.deRefJSON(value)
if err != nil {
iteratorErr = err
return false
}
res[key.String()] = r
return true
})
if iteratorErr != nil {
return nil, iteratorErr
}
return res, nil
}
if j.IsBool() {
return j.Value(), nil
}
switch j.Type {
case gjson.String:
v := j.String()
if v[0] != '@' {
return v, nil
}
return r.Fn(v)
case gjson.Number:
fallthrough
case gjson.Null:
return j.Value(), nil
case gjson.JSON:
fallthrough
case gjson.True:
fallthrough
case gjson.False:
fallthrough
default:
panic(fmt.Sprintf("unhandled type: %v", j.Type.String()))
}
}

0 comments on commit f19539f

Please sign in to comment.