refactor: Move from global state to functions (#53)
This commit represents a few experiments of features I've used in Cobra 1. Uses cli.GenericFlag to encapsulate parsing and validation of flag values at parse time. This removes the burden from the individual CLI commands to parse and validate args and options. 2. Add influxid.ID that may be used by any flag that requires an Influx ID. influxid.ID parses and validates string value is a valid ID, removing this burden from individual commands and ensuring valid values before the command actions begins. 3. Binds cli.Flags directly to params structures to directly capture the values when parsing flags. 4. Moves from global state to local builder functions for the majority of the commands. This allows the commands to bind to flag variables reducing the repeated ctx.String(), ctx.Int(), etc 5. Leverages the BeforeFunc to create middleware and inject the CLI and API client into commands, saving the repeated boilerplate across all of the instantiated commands. This is extensible, so additional middleware can be appends using the middleware.WithBeforeFns
This commit is contained in:
25
internal/api/write_precision.go
Normal file
25
internal/api/write_precision.go
Normal file
@ -0,0 +1,25 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func (v WritePrecision) String() string {
|
||||
return string(v)
|
||||
}
|
||||
|
||||
func (v *WritePrecision) Set(s string) error {
|
||||
switch s {
|
||||
case "ms":
|
||||
*v = WRITEPRECISION_MS
|
||||
case "s":
|
||||
*v = WRITEPRECISION_S
|
||||
case "us":
|
||||
*v = WRITEPRECISION_US
|
||||
case "ns":
|
||||
*v = WRITEPRECISION_NS
|
||||
default:
|
||||
return fmt.Errorf("unsupported precision: %q", s)
|
||||
}
|
||||
return nil
|
||||
}
|
@ -29,6 +29,33 @@ const (
|
||||
InputFormatLP
|
||||
)
|
||||
|
||||
func (i *InputFormat) Set(v string) error {
|
||||
switch v {
|
||||
case "":
|
||||
*i = InputFormatDerived
|
||||
case "lp":
|
||||
*i = InputFormatLP
|
||||
case "csv":
|
||||
*i = InputFormatCSV
|
||||
default:
|
||||
return fmt.Errorf("unsupported format: %q", v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i InputFormat) String() string {
|
||||
switch i {
|
||||
case InputFormatLP:
|
||||
return "lp"
|
||||
case InputFormatCSV:
|
||||
return "csv"
|
||||
case InputFormatDerived:
|
||||
fallthrough
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
type InputCompression int
|
||||
|
||||
const (
|
||||
@ -37,6 +64,33 @@ const (
|
||||
InputCompressionNone
|
||||
)
|
||||
|
||||
func (i *InputCompression) Set(v string) error {
|
||||
switch v {
|
||||
case "":
|
||||
*i = InputCompressionDerived
|
||||
case "none":
|
||||
*i = InputCompressionNone
|
||||
case "gzip":
|
||||
*i = InputCompressionGZIP
|
||||
default:
|
||||
return fmt.Errorf("unsupported compression: %q", v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i InputCompression) String() string {
|
||||
switch i {
|
||||
case InputCompressionNone:
|
||||
return "none"
|
||||
case InputCompressionGZIP:
|
||||
return "gzip"
|
||||
case InputCompressionDerived:
|
||||
fallthrough
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
type MultiInputLineReader struct {
|
||||
StdIn io.Reader
|
||||
HttpClient HttpClient
|
||||
|
@ -16,12 +16,8 @@ type Throttler struct {
|
||||
bytesPerSecond float64
|
||||
}
|
||||
|
||||
func NewThrottler(rate string) (*Throttler, error) {
|
||||
bytesPerSec, err := ToBytesPerSecond(rate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Throttler{bytesPerSecond: bytesPerSec}, nil
|
||||
func NewThrottler(bytesPerSec BytesPerSec) *Throttler {
|
||||
return &Throttler{bytesPerSecond: float64(bytesPerSec)}
|
||||
}
|
||||
|
||||
func (t *Throttler) Throttle(ctx context.Context, in io.Reader) io.Reader {
|
||||
@ -41,10 +37,25 @@ var rateLimitRegexp = regexp.MustCompile(`^(\d*\.?\d*)(B|kB|MB)/?(\d*)?(s|sec|m|
|
||||
var bytesUnitMultiplier = map[string]float64{"B": 1, "kB": 1024, "MB": 1_048_576}
|
||||
var timeUnitMultiplier = map[string]float64{"s": 1, "sec": 1, "m": 60, "min": 60}
|
||||
|
||||
type BytesPerSec float64
|
||||
|
||||
func (b *BytesPerSec) Set(v string) (err error) {
|
||||
bps, err := ToBytesPerSecond(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*b = BytesPerSec(bps)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b BytesPerSec) String() string {
|
||||
return strconv.FormatFloat(float64(b), 'E', -1, 64)
|
||||
}
|
||||
|
||||
// ToBytesPerSecond converts rate from string to number. The supplied string
|
||||
// value format must be COUNT(B|kB|MB)/TIME(s|sec|m|min) with / and TIME being optional.
|
||||
// All spaces are ignored, they can help with formatting. Examples: "5 MB / 5 min", 17kbs. 5.1MB5m.
|
||||
func ToBytesPerSecond(rateLimit string) (float64, error) {
|
||||
func ToBytesPerSecond(rateLimit string) (BytesPerSec, error) {
|
||||
// ignore all spaces
|
||||
strVal := strings.ReplaceAll(rateLimit, " ", "")
|
||||
if len(strVal) == 0 {
|
||||
@ -74,5 +85,5 @@ func ToBytesPerSecond(rateLimit string) (float64, error) {
|
||||
time = float64(int64Val)
|
||||
}
|
||||
time = time * timeUnitMultiplier[matches[4]]
|
||||
return bytes / time, nil
|
||||
return BytesPerSec(bytes / time), nil
|
||||
}
|
||||
|
@ -14,8 +14,9 @@ func TestThrottlerPassthrough(t *testing.T) {
|
||||
// Hard to test that rate-limiting actually works, so we just check
|
||||
// that no data is lost.
|
||||
in := "Hello world!"
|
||||
throttler, err := throttler.NewThrottler("1B/s")
|
||||
bps, err := throttler.ToBytesPerSecond("1B/s")
|
||||
require.NoError(t, err)
|
||||
throttler := throttler.NewThrottler(bps)
|
||||
r := throttler.Throttle(context.Background(), strings.NewReader(in))
|
||||
|
||||
out := bytes.Buffer{}
|
||||
@ -72,7 +73,7 @@ func TestToBytesPerSecond(t *testing.T) {
|
||||
t.Run(test.in, func(t *testing.T) {
|
||||
bytesPerSec, err := throttler.ToBytesPerSecond(test.in)
|
||||
if len(test.error) == 0 {
|
||||
require.Equal(t, test.out, bytesPerSec)
|
||||
require.Equal(t, test.out, float64(bytesPerSec))
|
||||
require.Nil(t, err)
|
||||
} else {
|
||||
require.NotNil(t, err)
|
||||
|
Reference in New Issue
Block a user