Skip to content

Commit

Permalink
wip: configio converters
Browse files Browse the repository at this point in the history
- More declarative syntax
- Better reusability & maintainability: no more single complex
  parsing function
  • Loading branch information
GregoryAlbouy committed Apr 1, 2023
1 parent 0dea530 commit 7618df3
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 81 deletions.
133 changes: 133 additions & 0 deletions configio/converters.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package configio

import (
"bytes"
"errors"
"fmt"
"io"
"net/http"
"time"

"github.com/benchttp/engine/benchttp"
)

type (
converter func(representation, *benchttp.Runner) error
requestConverter func(representation, *http.Request) error
)

var fieldConverters = []converter{
func(repr representation, dst *benchttp.Runner) error {
req := dst.Request
if req == nil {
req = &http.Request{}
}
for _, p := range requestConverters {
if err := p(repr, req); err != nil {
return err
}
}
dst.Request = req
return nil
},
func(repr representation, dst *benchttp.Runner) error {
for _, p := range runnerParsers {
if err := p(repr, dst); err != nil {
return err
}
}
return nil
},
}

var requestParser = func(repr representation, dst *benchttp.Runner) error {
req := &http.Request{}
for _, fieldParser := range requestConverters {
if err := fieldParser(repr, req); err != nil {
return err
}
}
dst.Request = req
return nil
}

var requestConverters = []requestConverter{
func(repr representation, dst *http.Request) error {
return setString(repr.Request.Method, &dst.Method)
},
func(repr representation, dst *http.Request) error {
if rawURL := repr.Request.URL; rawURL != nil {
parsedURL, err := parseAndBuildURL(*rawURL, repr.Request.QueryParams)
if err != nil {
return fmt.Errorf(`configio: invalid url: %q`, *rawURL)
}
dst.URL = parsedURL
}
return nil
},
func(repr representation, dst *http.Request) error {
if header := repr.Request.Header; len(header) != 0 {
httpHeader := http.Header{}
for key, val := range header {
httpHeader[key] = val
}
dst.Header = httpHeader
}
return nil
},
func(repr representation, dst *http.Request) error {
if body := repr.Request.Body; body != nil {
switch body.Type {
case "raw":
dst.Body = io.NopCloser(bytes.NewReader([]byte(body.Content)))
default:
return errors.New(`configio: request.body.type: only "raw" accepted`)
}
}
return nil
},
}

var runnerParsers = map[string]converter{
"requests": func(repr representation, dst *benchttp.Runner) error {
return setInt(repr.Runner.Requests, &dst.Requests)
},
"concurrency": func(repr representation, dst *benchttp.Runner) error {
return setInt(repr.Runner.Concurrency, &dst.Concurrency)
},
"interval": func(repr representation, dst *benchttp.Runner) error {
return setOptionalDuration(repr.Runner.Interval, &dst.Interval)
},
"requestTimeout": func(repr representation, dst *benchttp.Runner) error {
return setOptionalDuration(repr.Runner.RequestTimeout, &dst.RequestTimeout)
},
"globalTimeout": func(repr representation, dst *benchttp.Runner) error {
return setOptionalDuration(repr.Runner.GlobalTimeout, &dst.GlobalTimeout)
},
}

func setInt(src, dst *int) error {
if src != nil {
*dst = *src
}
return nil
}

func setString(src, dst *string) error {
if src != nil {
*dst = *src
}
return nil
}

func setOptionalDuration(src *string, dst *time.Duration) error {
if src == nil {
return nil
}
parsed, err := parseOptionalDuration(*src)
if err != nil {
return err
}
*dst = parsed
return nil
}
85 changes: 4 additions & 81 deletions configio/representation.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
package configio

import (
"bytes"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"strconv"
"time"
Expand Down Expand Up @@ -57,86 +53,13 @@ func (repr representation) validate() error {
// and stores any non-nil field value into the corresponding field
// of dst.
func (repr representation) parseAndMutate(dst *benchttp.Runner) error {
if err := repr.parseRequestInto(dst); err != nil {
return err
}
if err := repr.parseRunnerInto(dst); err != nil {
return err
}
return repr.parseTestsInto(dst)
}

func (repr representation) parseRequestInto(dst *benchttp.Runner) error {
if dst.Request == nil {
dst.Request = &http.Request{}
}

if method := repr.Request.Method; method != nil {
dst.Request.Method = *method
}

if rawURL := repr.Request.URL; rawURL != nil {
parsedURL, err := parseAndBuildURL(*rawURL, repr.Request.QueryParams)
if err != nil {
return fmt.Errorf(`configio: invalid url: %q`, *rawURL)
}
dst.Request.URL = parsedURL
}

if header := repr.Request.Header; len(header) != 0 {
httpHeader := http.Header{}
for key, val := range header {
httpHeader[key] = val
}
dst.Request.Header = httpHeader
}

if body := repr.Request.Body; body != nil {
switch body.Type {
case "raw":
dst.Request.Body = io.NopCloser(bytes.NewReader([]byte(body.Content)))
default:
return errors.New(`configio: request.body.type: only "raw" accepted`)
}
}

return nil
}

func (repr representation) parseRunnerInto(dst *benchttp.Runner) error {
if requests := repr.Runner.Requests; requests != nil {
dst.Requests = *requests
}

if concurrency := repr.Runner.Concurrency; concurrency != nil {
dst.Concurrency = *concurrency
}

if interval := repr.Runner.Interval; interval != nil {
parsedInterval, err := parseOptionalDuration(*interval)
if err != nil {
return err
}
dst.Interval = parsedInterval
}

if requestTimeout := repr.Runner.RequestTimeout; requestTimeout != nil {
parsedTimeout, err := parseOptionalDuration(*requestTimeout)
if err != nil {
return err
}
dst.RequestTimeout = parsedTimeout
}

if globalTimeout := repr.Runner.GlobalTimeout; globalTimeout != nil {
parsedGlobalTimeout, err := parseOptionalDuration(*globalTimeout)
if err != nil {
for _, converter := range fieldConverters {
if err := converter(repr, dst); err != nil {
return err
}
dst.GlobalTimeout = parsedGlobalTimeout
}

return nil
// TODO: use converter for tests
return repr.parseTestsInto(dst)
}

func (repr representation) parseTestsInto(dst *benchttp.Runner) error {
Expand Down

0 comments on commit 7618df3

Please sign in to comment.