New Upstream Release - golang-github-anacrolix-tagflag
Ready changes
Summary
Merged new upstream version: 0.0.0-20230507-45431b4+ds (was: 0.0.0-20220926-a6f8d8a+ds).
Resulting package
Built on 2023-06-03T00:04 (took 6m42s)
The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:
apt install -t fresh-releases golang-github-anacrolix-tagflag-dev
Lintian Result
- golang-github-anacrolix-tagflag-dev_0.0.0-20230507-45431b4+ds-1~jan+nur1_all.deb
- golang-github-anacrolix-tagflag_0.0.0-20230507-45431b4+ds-1~jan+nur1.dsc
- golang-github-anacrolix-tagflag_0.0.0-20230507-45431b4+ds-1~jan+nur1_amd64.buildinfo
- golang-github-anacrolix-tagflag_0.0.0-20230507-45431b4+ds-1~jan+nur1_amd64.changes
Diff
diff --git a/README.md b/README.md
index bf7ee54..f53ebef 100644
--- a/README.md
+++ b/README.md
@@ -2,4 +2,9 @@
[![GoDoc](https://godoc.org/github.com/anacrolix/tagflag?status.svg)](https://godoc.org/github.com/anacrolix/tagflag)
-See the thorough package documentation.
+See the thorough package documentation on [GoDoc](https://godoc.org/github.com/anacrolix/tagflag). I know you want to see this in the README, but just go to the GoDoc.
+
+## Similar projects
+
+ * https://github.com/jaffee/commandeer
+ * https://github.com/octago/sflags
diff --git a/arg.go b/arg.go
index b6e417b..0dc36df 100644
--- a/arg.go
+++ b/arg.go
@@ -19,9 +19,9 @@ func (me arg) hasZeroValue() bool {
}
func (me arg) marshal(s string, explicitValue bool) error {
- m := valueMarshaler(me.value)
- if !explicitValue && m.RequiresExplicitValue() {
+ m := valueMarshaler(me.value.Type())
+ if m.RequiresExplicitValue() && !explicitValue {
return userError{fmt.Sprintf("explicit value required (%s%s=VALUE)", flagPrefix, me.name)}
}
- return valueMarshaler(me.value).Marshal(me.value, s)
+ return m.Marshal(me.value, s)
}
diff --git a/builtin.go b/builtin.go
index f05f9dc..46d21f2 100644
--- a/builtin.go
+++ b/builtin.go
@@ -1,10 +1,16 @@
package tagflag
import (
+ "errors"
+ "fmt"
"net"
"net/url"
"reflect"
+ "strconv"
+ "strings"
"time"
+
+ "golang.org/x/xerrors"
)
var builtinMarshalers = map[reflect.Type]marshaler{}
@@ -45,12 +51,61 @@ func init() {
if s == "" {
return nil, nil
}
- return net.ResolveTCPAddr("tcp", s)
+ var ret net.TCPAddr
+ retAlt, err := parseIpPortZone(s)
+ if err != nil {
+ return nil, err
+ }
+ ret = net.TCPAddr(retAlt)
+ return &ret, err
}, true)
addBuiltinDynamicMarshaler(func(s string) (time.Duration, error) {
return time.ParseDuration(s)
}, false)
- addBuiltinDynamicMarshaler(func(s string) net.IP {
- return net.ParseIP(s)
+ addBuiltinDynamicMarshaler(func(s string) (ip net.IP, err error) {
+ ip = net.ParseIP(s)
+ if ip == nil {
+ err = fmt.Errorf("failed to parse IP")
+ }
+ return
}, false)
}
+
+func parseIpAddr(host string) (ret net.IPAddr, err error) {
+ ss := strings.SplitN(host, "%", 2)
+ ret.IP = net.ParseIP(ss[0])
+ if ret.IP == nil && ss[0] != "" {
+ err = errors.New("error parsing IP")
+ return
+ }
+ if len(ss) >= 2 {
+ ret.Zone = ss[1]
+ }
+ return
+}
+
+type ipPortZone struct {
+ IP net.IP
+ Port int
+ Zone string
+}
+
+func parseIpPortZone(hostport string) (ret ipPortZone, err error) {
+ host, port, err := net.SplitHostPort(hostport)
+ if err != nil {
+ return
+ }
+ portInt64, err := strconv.ParseInt(port, 10, 0)
+ if err != nil {
+ return
+ }
+ ret.Port = int(portInt64)
+ ipAddr, err := parseIpAddr(host)
+ if err != nil {
+ err = xerrors.Errorf("parsing host %q: %w", host, err)
+ return
+ }
+ ret.IP = ipAddr.IP
+ ret.Zone = ipAddr.Zone
+ return
+}
diff --git a/bytes.go b/bytes.go
index c34f5d2..f8ba1d2 100644
--- a/bytes.go
+++ b/bytes.go
@@ -1,12 +1,19 @@
package tagflag
-import "github.com/dustin/go-humanize"
+import (
+ "encoding"
+
+ "github.com/dustin/go-humanize"
+)
// A nice builtin type that will marshal human readable byte quantities to
// int64. For example 100GB. See https://godoc.org/github.com/dustin/go-humanize.
type Bytes int64
-var _ Marshaler = new(Bytes)
+var (
+ _ Marshaler = (*Bytes)(nil)
+ _ encoding.TextUnmarshaler = (*Bytes)(nil)
+)
func (me *Bytes) Marshal(s string) (err error) {
ui64, err := humanize.ParseBytes(s)
@@ -17,7 +24,11 @@ func (me *Bytes) Marshal(s string) (err error) {
return
}
-func (Bytes) RequiresExplicitValue() bool {
+func (me *Bytes) UnmarshalText(text []byte) error {
+ return me.Marshal(string(text))
+}
+
+func (*Bytes) RequiresExplicitValue() bool {
return false
}
diff --git a/cases_test.go b/cases_test.go
new file mode 100644
index 0000000..fd55b01
--- /dev/null
+++ b/cases_test.go
@@ -0,0 +1,53 @@
+package tagflag
+
+import (
+ "reflect"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+type parseCase struct {
+ args []string
+ err func(*testing.T, error)
+ expected interface{}
+}
+
+func noErrorCase(expected interface{}, args ...string) parseCase {
+ return parseCase{args: args, expected: expected}
+}
+
+func errorCase(err error, args ...string) parseCase {
+ return parseCase{
+ args: args,
+ err: func(t *testing.T, actualErr error) {
+ assert.EqualValues(t, err, actualErr)
+ },
+ }
+}
+
+func anyErrorCase(args ...string) parseCase {
+ return parseCase{
+ args: args,
+ err: func(t *testing.T, err error) {
+ assert.Error(t, err)
+ },
+ }
+}
+
+func (me parseCase) Run(t *testing.T, newCmd func() interface{}) {
+ cmd := newCmd()
+ err := ParseErr(cmd, me.args)
+ if me.err == nil {
+ assert.NoError(t, err)
+ assert.EqualValues(t, me.expected, reflect.ValueOf(cmd).Elem().Interface(), "%v", me)
+ } else {
+ me.err(t, err)
+ }
+}
+
+func RunCases(t *testing.T, cases []parseCase, newCmd func() interface{}) {
+ for _, _case := range cases {
+ _case.Run(t, newCmd)
+ }
+}
diff --git a/debian/changelog b/debian/changelog
index 25776a1..c7de528 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+golang-github-anacrolix-tagflag (0.0.0-20230507-45431b4+ds-1) UNRELEASED; urgency=low
+
+ * New upstream release.
+ * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk> Fri, 02 Jun 2023 23:58:38 -0000
+
golang-github-anacrolix-tagflag (0.0.0-20180109-2146c8d-4) unstable; urgency=medium
[ Debian Janitor ]
diff --git a/excess.go b/excess.go
index 25467ea..0e60770 100644
--- a/excess.go
+++ b/excess.go
@@ -2,6 +2,8 @@ package tagflag
import "fmt"
+// The error returned if there are fields in a struct after ExcessArgs.
var ErrFieldsAfterExcessArgs = fmt.Errorf("field(s) after %T", ExcessArgs{})
+// This should be added to the end of a struct to soak up any arguments that didn't fit sooner.
type ExcessArgs []string
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..261b498
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,16 @@
+module github.com/anacrolix/tagflag
+
+go 1.12
+
+require (
+ github.com/anacrolix/missinggo v1.3.0 // indirect
+ github.com/anacrolix/missinggo/v2 v2.6.0
+ github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8
+ github.com/dustin/go-humanize v1.0.0
+ github.com/huandu/xstrings v1.3.2
+ github.com/kr/pretty v0.3.0 // indirect
+ github.com/pkg/errors v0.9.1
+ github.com/rogpeppe/go-internal v1.8.0 // indirect
+ github.com/stretchr/testify v1.7.0
+ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..8410312
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,259 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+crawshaw.io/iox v0.0.0-20181124134642-c51c3df30797/go.mod h1:sXBiorCo8c46JlQV3oXPKINnZ8mcqnye1EkVkqsectk=
+crawshaw.io/sqlite v0.3.2/go.mod h1:igAO5JulrQ1DbdZdtVq48mnZUBAPOeFzer7VhDWNtW4=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/RoaringBitmap/roaring v0.4.7/go.mod h1:8khRDP4HmeXns4xIj9oGrKSz7XTQiJx2zgh7AcNke4w=
+github.com/RoaringBitmap/roaring v0.4.17/go.mod h1:D3qVegWTmfCaX4Bl5CrBE9hfrSrrXIr8KVNvRsDi1NI=
+github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo=
+github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
+github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/anacrolix/envpprof v0.0.0-20180404065416-323002cec2fa/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c=
+github.com/anacrolix/envpprof v1.0.0/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c=
+github.com/anacrolix/envpprof v1.1.0/go.mod h1:My7T5oSqVfEn4MD4Meczkw/f5lSIndGAKu/0SM/rkf4=
+github.com/anacrolix/log v0.3.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU=
+github.com/anacrolix/log v0.6.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU=
+github.com/anacrolix/missinggo v1.1.0/go.mod h1:MBJu3Sk/k3ZfGYcS7z18gwfu72Ey/xopPFJJbTi5yIo=
+github.com/anacrolix/missinggo v1.1.2-0.20190815015349-b888af804467/go.mod h1:MBJu3Sk/k3ZfGYcS7z18gwfu72Ey/xopPFJJbTi5yIo=
+github.com/anacrolix/missinggo v1.2.1/go.mod h1:J5cMhif8jPmFoC3+Uvob3OXXNIhOUikzMt+uUjeM21Y=
+github.com/anacrolix/missinggo v1.3.0 h1:06HlMsudotL7BAELRZs0yDZ4yVXsHXGi323QBjAVASw=
+github.com/anacrolix/missinggo v1.3.0/go.mod h1:bqHm8cE8xr+15uVfMG3BFui/TxyB6//H5fwlq/TeqMc=
+github.com/anacrolix/missinggo/perf v1.0.0/go.mod h1:ljAFWkBuzkO12MQclXzZrosP5urunoLS0Cbvb4V0uMQ=
+github.com/anacrolix/missinggo/v2 v2.2.0/go.mod h1:o0jgJoYOyaoYQ4E2ZMISVa9c88BbUBVQQW4QeRkNCGY=
+github.com/anacrolix/missinggo/v2 v2.5.1/go.mod h1:WEjqh2rmKECd0t1VhQkLGTdIWXO6f6NLjp5GlMZ+6FA=
+github.com/anacrolix/missinggo/v2 v2.6.0 h1:kHkn6nLy1isWYV4mthZX8itV1bRd2mwFVuXrxzJ4VX0=
+github.com/anacrolix/missinggo/v2 v2.6.0/go.mod h1:2IZIvmRTizALNYFYXsPR7ofXPzJgyBpKZ4kMqMEICkI=
+github.com/anacrolix/stm v0.2.0/go.mod h1:zoVQRvSiGjGoTmbM0vSLIiaKjWtNPeTvXUSdJQA4hsg=
+github.com/anacrolix/tagflag v0.0.0-20180109131632-2146c8d41bf0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw=
+github.com/anacrolix/tagflag v1.0.0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw=
+github.com/anacrolix/tagflag v1.1.0/go.mod h1:Scxs9CV10NQatSmbyjqmqmeQNwGzlNe0CMUMIxqHIG8=
+github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
+github.com/benbjohnson/immutable v0.2.0/go.mod h1:uc6OHo6PN2++n98KHLxW8ef4W42ylHiQSENghE1ezxI=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/bradfitz/iter v0.0.0-20140124041915-454541ec3da2/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo=
+github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo=
+github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 h1:GKTyiRCL6zVf5wWaqKnf+7Qs6GbEPfd4iMOitWzXJx8=
+github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8/go.mod h1:spo1JLcs67NmW1aVLEgtA8Yy1elc+X8y5SRW1sFW4Og=
+github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
+github.com/dustin/go-humanize v0.0.0-20180421182945-02af3965c54e/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
+github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
+github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
+github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
+github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
+github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
+github.com/glycerine/goconvey v0.0.0-20180728074245-46e3a41ad493/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
+github.com/glycerine/goconvey v0.0.0-20190315024820-982ee783a72e/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
+github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
+github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
+github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gopherjs/gopherjs v0.0.0-20190309154008-847fc94819f9/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
+github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
+github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo=
+github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
+github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
+github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
+github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
+github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
+github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
+github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
+github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=
+github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
+github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
+github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
+github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
+github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
+github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
+github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
+github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
+github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
+github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
+github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
+github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
+github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8=
+github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
+github.com/smartystreets/goconvey v0.0.0-20190306220146-200a235640ff/go.mod h1:KSQcGKpxUMHk3nbYzs/tIBAM2iDooCn0BmttHOJEbLs=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
+github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
+github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
+github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
+github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
+go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
+go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
+go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/marshalers.go b/marshalers.go
index 0e8b05d..b932b84 100644
--- a/marshalers.go
+++ b/marshalers.go
@@ -4,10 +4,14 @@ import (
"fmt"
"reflect"
"strconv"
+
+ "github.com/pkg/errors"
)
+// TODO: Perhaps this should embed encoding.TextUnmarshaler instead.
type Marshaler interface {
Marshal(in string) error
+ // Must have and ignore a pointer receiver.
RequiresExplicitValue() bool
}
@@ -34,9 +38,10 @@ func (me dynamicMarshaler) RequiresExplicitValue() bool {
type defaultMarshaler struct{}
func (defaultMarshaler) Marshal(v reflect.Value, s string) error {
- if v.Kind() == reflect.Slice {
+ switch v.Kind() {
+ case reflect.Slice:
n := reflect.New(v.Type().Elem())
- m := valueMarshaler(n.Elem())
+ m := valueMarshaler(n.Elem().Type())
if m == nil {
return fmt.Errorf("can't marshal type %s", n.Elem().Type())
}
@@ -46,8 +51,6 @@ func (defaultMarshaler) Marshal(v reflect.Value, s string) error {
}
v.Set(reflect.Append(v, n.Elem()))
return nil
- }
- switch v.Kind() {
case reflect.Int:
x, err := strconv.ParseInt(s, 0, 0)
v.SetInt(x)
@@ -63,6 +66,26 @@ func (defaultMarshaler) Marshal(v reflect.Value, s string) error {
case reflect.String:
v.SetString(s)
return nil
+ case reflect.Array:
+ if v.Type().Elem().Kind() == reflect.Uint8 {
+ if len(s) == 2*v.Len() {
+ var sl []byte = v.Slice(0, v.Len()).Interface().([]byte)
+ // log.Println(cap(sl), len(sl))
+ // hex.DecodeString(s)
+ _, err := fmt.Sscanf(s, "%x", &sl)
+ // log.Println(cap(sl), sl, err)
+ if err != nil {
+ return errors.Wrapf(err, "scanning hex")
+ }
+ // Seems the slice moves as part of the decoding :|
+ reflect.Copy(v, reflect.ValueOf(sl))
+ } else {
+ return errors.Errorf("argument has unhandled length %d", len(s))
+ }
+ return nil
+ } else {
+ return errors.Errorf("unhandled array elem type: %s", v.Type().String())
+ }
default:
return fmt.Errorf("unhandled builtin type: %s", v.Type().String())
}
@@ -71,3 +94,17 @@ func (defaultMarshaler) Marshal(v reflect.Value, s string) error {
func (defaultMarshaler) RequiresExplicitValue() bool {
return true
}
+
+type ptrMarshaler struct {
+ inner marshaler
+}
+
+func (me ptrMarshaler) Marshal(v reflect.Value, s string) error {
+ elemValue := reflect.New(v.Type().Elem())
+ v.Set(elemValue)
+ return me.inner.Marshal(elemValue.Elem(), s)
+}
+
+func (me ptrMarshaler) RequiresExplicitValue() bool {
+ return false
+}
diff --git a/misc.go b/misc.go
index bf3b4c9..326fb37 100644
--- a/misc.go
+++ b/misc.go
@@ -2,9 +2,8 @@ package tagflag
import (
"reflect"
- "regexp"
"strconv"
- "strings"
+ "unicode"
"github.com/bradfitz/iter"
)
@@ -26,24 +25,30 @@ func foreachStructField(_struct reflect.Value, f func(fv reflect.Value, sf refle
}
func canMarshal(f reflect.Value) bool {
- return valueMarshaler(f) != nil
+ return valueMarshaler(f.Type()) != nil
}
// Returns a marshaler for the given value, or nil if there isn't one.
-func valueMarshaler(v reflect.Value) marshaler {
- if v.CanAddr() {
- if am, ok := v.Addr().Interface().(Marshaler); ok {
- return dynamicMarshaler{
- marshal: func(_ reflect.Value, s string) error { return am.Marshal(s) },
- explicitValueRequired: am.RequiresExplicitValue(),
- }
+func valueMarshaler(t reflect.Type) marshaler {
+ if zm, ok := reflect.Zero(reflect.PtrTo(t)).Interface().(Marshaler); ok {
+ return dynamicMarshaler{
+ marshal: func(v reflect.Value, s string) error {
+ return v.Addr().Interface().(Marshaler).Marshal(s)
+ },
+ explicitValueRequired: zm.RequiresExplicitValue(),
}
}
- if bm, ok := builtinMarshalers[v.Type()]; ok {
+ if bm, ok := builtinMarshalers[t]; ok {
return bm
}
- switch v.Kind() {
- case reflect.Ptr, reflect.Struct:
+ switch t.Kind() {
+ case reflect.Ptr:
+ m := valueMarshaler(t.Elem())
+ if m == nil {
+ return nil
+ }
+ return ptrMarshaler{m}
+ case reflect.Struct:
return nil
case reflect.Bool:
return dynamicMarshaler{
@@ -65,19 +70,16 @@ func valueMarshaler(v reflect.Value) marshaler {
// Turn a struct field name into a flag name. In particular this lower cases
// leading acronyms, and the first capital letter.
func fieldFlagName(fieldName string) flagNameComponent {
- return flagNameComponent(func() string {
- // TCP
- if ss := regexp.MustCompile("^[[:upper:]]{2,}$").FindStringSubmatch(fieldName); ss != nil {
- return strings.ToLower(ss[0])
- }
- // TCPAddr
- if ss := regexp.MustCompile("^([[:upper:]]+)([[:upper:]][^[:upper:]].*?)$").FindStringSubmatch(fieldName); ss != nil {
- return strings.ToLower(ss[1]) + ss[2]
- }
- // Addr
- if ss := regexp.MustCompile("^([[:upper:]])(.*)$").FindStringSubmatch(fieldName); ss != nil {
- return strings.ToLower(ss[1]) + ss[2]
+ return flagNameComponent(func() (ret []rune) {
+ fieldNameRunes := []rune(fieldName)
+ for i, r := range fieldNameRunes {
+ prevUpper := func() bool { return unicode.IsUpper(fieldNameRunes[i-1]) }
+ nextUpper := func() bool { return unicode.IsUpper(fieldNameRunes[i+1]) }
+ if i == 0 || (prevUpper() && (i == len(fieldNameRunes)-1 || nextUpper())) {
+ r = unicode.ToLower(r)
+ }
+ ret = append(ret, r)
}
- panic(fieldName)
+ return
}())
}
diff --git a/parseopt.go b/parseopt.go
index fb3124d..f3c8da2 100644
--- a/parseopt.go
+++ b/parseopt.go
@@ -1,23 +1,35 @@
package tagflag
-type parseOpt func(p *parser)
+type parseOpt func(p *Parser)
// Don't perform default behaviour if -h or -help are passed.
func NoDefaultHelp() parseOpt {
- return func(p *parser) {
+ return func(p *Parser) {
p.noDefaultHelp = true
}
}
// Provides a description for the program to be shown in the usage message.
func Description(desc string) parseOpt {
- return func(p *parser) {
+ return func(p *Parser) {
p.description = desc
}
}
func Program(name string) parseOpt {
- return func(p *parser) {
+ return func(p *Parser) {
p.program = name
}
}
+
+func ParseIntermixed(enabled bool) parseOpt {
+ return func(p *Parser) {
+ p.parseIntermixed = enabled
+ }
+}
+
+func Parent(parent *Parser) parseOpt {
+ return func(p *Parser) {
+ p.parent = parent
+ }
+}
diff --git a/parser.go b/parser.go
index 675a0e9..27a8586 100644
--- a/parser.go
+++ b/parser.go
@@ -2,21 +2,26 @@ package tagflag
import (
"fmt"
- "log"
"reflect"
"strings"
- "github.com/anacrolix/missinggo/slices"
+ "github.com/anacrolix/missinggo/v2/slices"
"github.com/huandu/xstrings"
+ "golang.org/x/xerrors"
)
-type parser struct {
- // The value from which the parser is built, and values are assigned.
+type Parser struct {
+ // The value from which the Parser is built, and values are assigned.
cmd interface{}
// Disables the default handling of -h and -help.
noDefaultHelp bool
program string
description string
+ // Whether the first non-option argument requires that all further arguments are to be treated
+ // as positional.
+ parseIntermixed bool
+ // The Parser that preceded this one, such as in sub-command relationship.
+ parent *Parser
posArgs []arg
// Maps -K=V to map[K]arg(V)
@@ -28,11 +33,11 @@ type parser struct {
numPos int
}
-func (p *parser) hasOptions() bool {
+func (p *Parser) hasOptions() bool {
return len(p.flags) != 0
}
-func (p *parser) parse(args []string) (err error) {
+func (p *Parser) parse(args []string) (err error) {
posOnly := false
for len(args) != 0 {
if p.excess != nil && p.nextPosArg() == nil {
@@ -47,8 +52,14 @@ func (p *parser) parse(args []string) (err error) {
}
if !posOnly && isFlag(a) {
err = p.parseFlag(a[1:])
+ if err != nil {
+ err = xerrors.Errorf("parsing flag %q: %w", a[1:], err)
+ }
} else {
err = p.parsePos(a)
+ if !p.parseIntermixed {
+ posOnly = true
+ }
}
if err != nil {
return
@@ -60,16 +71,17 @@ func (p *parser) parse(args []string) (err error) {
return
}
-func (p *parser) minPos() (min int) {
+func (p *Parser) minPos() (min int) {
for _, arg := range p.posArgs {
min += arg.arity.min
}
return
}
-func newParser(cmd interface{}, opts ...parseOpt) (p *parser, err error) {
- p = &parser{
- cmd: cmd,
+func newParser(cmd interface{}, opts ...parseOpt) (p *Parser, err error) {
+ p = &Parser{
+ cmd: cmd,
+ parseIntermixed: true,
}
for _, opt := range opts {
opt(p)
@@ -78,19 +90,22 @@ func newParser(cmd interface{}, opts ...parseOpt) (p *parser, err error) {
return
}
-func (p *parser) parseCmd() error {
+func (p *Parser) parseCmd() error {
if p.cmd == nil {
return nil
}
s := reflect.ValueOf(p.cmd).Elem()
+ for s.Kind() == reflect.Interface {
+ s = s.Elem()
+ }
if s.Kind() != reflect.Struct {
return fmt.Errorf("expected struct got %s", s.Type())
}
- return p.parseStruct(reflect.ValueOf(p.cmd).Elem(), nil)
+ return p.parseStruct(s, nil)
}
// Positional arguments are marked per struct.
-func (p *parser) parseStruct(st reflect.Value, path []flagNameComponent) (err error) {
+func (p *Parser) parseStruct(st reflect.Value, path []flagNameComponent) (err error) {
posStarted := false
foreachStructField(st, func(f reflect.Value, sf reflect.StructField) (stop bool) {
if !posStarted && f.Type() == reflect.TypeOf(StartPos{}) {
@@ -119,13 +134,15 @@ func (p *parser) parseStruct(st reflect.Value, path []flagNameComponent) (err er
}
return err != nil
}
- if f.Kind() == reflect.Struct {
- if canMarshal(f.Addr()) {
- err = fmt.Errorf("field %q has type %s, did you mean to use %s?", sf.Name, f.Type(), f.Addr().Type())
- return true
- }
- err = p.parseStruct(f, append(path, structFieldFlagNameComponent(sf)))
- return err != nil
+ var parsed bool
+ parsed, err = p.parseEmbeddedStruct(f, sf, path)
+ if err != nil {
+ err = fmt.Errorf("parsing embedded struct: %w", err)
+ stop = true
+ return
+ }
+ if parsed {
+ return false
}
err = fmt.Errorf("field has bad type: %v", f.Type())
return true
@@ -133,6 +150,25 @@ func (p *parser) parseStruct(st reflect.Value, path []flagNameComponent) (err er
return
}
+func (p *Parser) parseEmbeddedStruct(f reflect.Value, sf reflect.StructField, path []flagNameComponent) (parsed bool, err error) {
+ if f.Kind() == reflect.Ptr {
+ f = f.Elem()
+ }
+ if f.Kind() != reflect.Struct {
+ return
+ }
+ if canMarshal(f.Addr()) {
+ err = fmt.Errorf("field %q has type %s, but %s is marshalable", sf.Name, f.Type(), f.Addr().Type())
+ return
+ }
+ parsed = true
+ if !sf.Anonymous {
+ path = append(path, structFieldFlagNameComponent(sf))
+ }
+ err = p.parseStruct(f, path)
+ return
+}
+
func newArg(v reflect.Value, sf reflect.StructField, name string) arg {
return arg{
arity: fieldArity(v, sf),
@@ -142,7 +178,7 @@ func newArg(v reflect.Value, sf reflect.StructField, name string) arg {
}
}
-func (p *parser) addPos(f reflect.Value, sf reflect.StructField, path []flagNameComponent) error {
+func (p *Parser) addPos(f reflect.Value, sf reflect.StructField, path []flagNameComponent) error {
p.posArgs = append(p.posArgs, newArg(f, sf, strings.ToUpper(xstrings.ToSnakeCase(sf.Name))))
return nil
}
@@ -153,7 +189,7 @@ func flagName(comps []flagNameComponent) string {
return strings.Join(ss, ".")
}
-func (p *parser) addFlag(f reflect.Value, sf reflect.StructField, path []flagNameComponent) error {
+func (p *Parser) addFlag(f reflect.Value, sf reflect.StructField, path []flagNameComponent) error {
name := flagName(append(path, structFieldFlagNameComponent(sf)))
if _, ok := p.flags[name]; ok {
return fmt.Errorf("flag %q defined more than once", name)
@@ -169,7 +205,7 @@ func isFlag(arg string) bool {
return len(arg) > 1 && arg[0] == '-'
}
-func (p *parser) parseFlag(s string) error {
+func (p *Parser) parseFlag(s string) error {
i := strings.IndexByte(s, '=')
k := s
v := ""
@@ -186,12 +222,12 @@ func (p *parser) parseFlag(s string) error {
}
err := flag.marshal(v, i != -1)
if err != nil {
- return fmt.Errorf("error setting flag %q: %s", k, err)
+ return xerrors.Errorf("parsing value %q for flag %q: %w", v, k, err)
}
return nil
}
-func (p *parser) indexPosArg(i int) *arg {
+func (p *Parser) indexPosArg(i int) *arg {
for _, arg := range p.posArgs {
if i < arg.arity.max {
return &arg
@@ -201,11 +237,11 @@ func (p *parser) indexPosArg(i int) *arg {
return nil
}
-func (p *parser) nextPosArg() *arg {
+func (p *Parser) nextPosArg() *arg {
return p.indexPosArg(p.numPos)
}
-func (p *parser) parsePos(s string) (err error) {
+func (p *Parser) parsePos(s string) (err error) {
arg := p.nextPosArg()
if arg == nil {
return userError{fmt.Sprintf("excess argument: %q", s)}
@@ -228,45 +264,7 @@ func structFieldFlagNameComponent(sf reflect.StructField) flagNameComponent {
return fieldFlagName(sf.Name)
}
-// Gets the reflect.Value for the nth positional argument.
-func posIndexValue(v reflect.Value, _i int) (ret reflect.Value, i int) {
- i = _i
- log.Println("posIndexValue", v.Type(), i)
- switch v.Kind() {
- case reflect.Ptr:
- return posIndexValue(v.Elem(), i)
- case reflect.Struct:
- posStarted := false
- foreachStructField(v, func(fv reflect.Value, sf reflect.StructField) bool {
- log.Println("posIndexValue struct field", fv, sf)
- if !posStarted {
- if fv.Type() == reflect.TypeOf(StartPos{}) {
- // log.Println("posStarted")
- posStarted = true
- }
- return true
- }
- ret, i = posIndexValue(fv, i)
- if ret.IsValid() {
- return false
- }
- return true
- })
- return
- case reflect.Slice:
- ret = v
- return
- default:
- if i == 0 {
- ret = v
- return
- }
- i--
- return
- }
-}
-
-func (p *parser) posWithHelp() (ret []arg) {
+func (p *Parser) posWithHelp() (ret []arg) {
for _, a := range p.posArgs {
if a.help != "" {
ret = append(ret, a)
diff --git a/tagflag.go b/tagflag.go
index 069ead8..aa25d1e 100644
--- a/tagflag.go
+++ b/tagflag.go
@@ -6,6 +6,8 @@ import (
"os"
"path/filepath"
"reflect"
+
+ "golang.org/x/xerrors"
)
// Struct fields after this one are considered positional arguments.
@@ -25,32 +27,34 @@ func ParseErr(cmd interface{}, args []string, opts ...parseOpt) (err error) {
// Parses the command-line arguments, exiting the process appropriately on
// errors or if usage is printed.
-func Parse(cmd interface{}, opts ...parseOpt) {
+func Parse(cmd interface{}, opts ...parseOpt) *Parser {
opts = append([]parseOpt{Program(filepath.Base(os.Args[0]))}, opts...)
- ParseArgs(cmd, os.Args[1:], opts...)
+ return ParseArgs(cmd, os.Args[1:], opts...)
}
-func ParseArgs(cmd interface{}, args []string, opts ...parseOpt) {
+// Like Parse, but operates on the given args instead.
+func ParseArgs(cmd interface{}, args []string, opts ...parseOpt) *Parser {
p, err := newParser(cmd, opts...)
if err == nil {
err = p.parse(args)
}
- if err == ErrDefaultHelp {
- p.printUsage(os.Stderr)
+ if xerrors.Is(err, ErrDefaultHelp) {
+ p.printUsage(os.Stdout)
os.Exit(0)
}
if err != nil {
- fmt.Fprintf(os.Stderr, "tagflag: %s\n", err)
+ fmt.Fprintf(os.Stderr, "tagflag: error parsing args: %v\n", err)
if _, ok := err.(userError); ok {
os.Exit(2)
}
os.Exit(1)
}
+ return p
}
func Unmarshal(arg string, v interface{}) error {
_v := reflect.ValueOf(v).Elem()
- m := valueMarshaler(_v)
+ m := valueMarshaler(_v.Type())
if m == nil {
return fmt.Errorf("can't unmarshal to type %s", _v.Type())
}
diff --git a/tagflag_test.go b/tagflag_test.go
index e49a8ca..708c779 100644
--- a/tagflag_test.go
+++ b/tagflag_test.go
@@ -4,10 +4,12 @@ import (
"log"
"net"
"os"
+ "reflect"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+ "golang.org/x/xerrors"
)
func TestBasic(t *testing.T) {
@@ -59,7 +61,7 @@ func TestBasic(t *testing.T) {
} {
var actual simpleCmd
err := ParseErr(&actual, _case.args)
- assert.EqualValues(t, _case.err, err)
+ assert.True(t, xerrors.Is(err, _case.err))
if _case.err != nil || _case.err != err {
// The value we got doesn't matter.
continue
@@ -69,6 +71,7 @@ func TestBasic(t *testing.T) {
}
func TestNotBasic(t *testing.T) {
+ t.Skip("outdated use of parseCase")
type cmd struct {
Seed bool
NoUpload bool
@@ -77,17 +80,8 @@ func TestNotBasic(t *testing.T) {
StartPos
Torrent []string `arity:"+"`
}
- for _, _case := range []struct {
- args []string
- err error
- expected cmd
- }{
- {nil, userError{`missing argument: "TORRENT"`}, cmd{}},
- {
- []string{"-seed"},
- userError{`missing argument: "TORRENT"`},
- cmd{},
- },
+ for _, _case := range []parseCase{
+ errorCase(userError{`missing argument: "TORRENT"`}, "-seed"),
{
[]string{"-seed", "a.torrent", "b.torrent"},
nil,
@@ -159,6 +153,7 @@ func TestUint(t *testing.T) {
}
func TestBasicPositionalArities(t *testing.T) {
+ t.Skip("outdated use of parseCase")
type cmd struct {
C bool
StartPos
@@ -166,11 +161,7 @@ func TestBasicPositionalArities(t *testing.T) {
B int64 `arity:"?"`
D []string `arity:"*"`
}
- for _, _case := range []struct {
- args []string
- err error
- expected cmd
- }{
+ for _, _case := range []parseCase{
// {nil, userError{`missing argument: "A"`}, cmd{}},
{[]string{"abc"}, nil, cmd{A: "abc"}},
{[]string{"abc", "123"}, nil, cmd{A: "abc", B: 123}},
@@ -220,20 +211,26 @@ func TestMain(m *testing.M) {
}
func TestDefaultLongFlagName(t *testing.T) {
- assert.EqualValues(t, "noUpload", fieldFlagName("NoUpload"))
- assert.EqualValues(t, "dht", fieldFlagName("DHT"))
- assert.EqualValues(t, "noIPv6", fieldFlagName("NoIPv6"))
- assert.EqualValues(t, "tcpAddr", fieldFlagName("TCPAddr"))
- assert.EqualValues(t, "addr", fieldFlagName("Addr"))
- assert.EqualValues(t, "v", fieldFlagName("V"))
- assert.EqualValues(t, "a", fieldFlagName("A"))
+ f := func(expected, start string) {
+ assert.EqualValues(t, expected, fieldFlagName(start), start)
+ }
+ f("noUpload", "NoUpload")
+ f("dht", "DHT")
+ f("noIPv6", "NoIPv6")
+ f("noIpv6", "NoIpv6")
+ f("tcpAddr", "TCPAddr")
+ f("addr", "Addr")
+ f("v", "V")
+ f("a", "A")
+ f("redisUrl", "RedisURL")
+ f("redisUrl", "redisURL")
}
func TestPrintUsage(t *testing.T) {
err := ParseErr(nil, []string{"-h"})
- assert.Equal(t, ErrDefaultHelp, err)
+ assert.True(t, xerrors.Is(err, ErrDefaultHelp), "%#v", err)
err = ParseErr(nil, []string{"-help"})
- assert.Equal(t, ErrDefaultHelp, err)
+ assert.True(t, xerrors.Is(err, ErrDefaultHelp))
}
func TestParseUnnamedTypes(t *testing.T) {
@@ -263,13 +260,6 @@ func TestPosArgSlice(t *testing.T) {
assert.EqualValues(t, []string{"a", "b", "c"}, cmd1.Args)
}
-func TestUnmarshallableTypes(t *testing.T) {
- var cmd1 struct {
- Wtf *int
- }
- assert.Contains(t, ParseErr(&cmd1, []string{"-wtf=yo"}).Error(), "*int")
-}
-
func TestTCPAddrNoExplicitValue(t *testing.T) {
var cmd struct {
Addr *net.TCPAddr
@@ -283,7 +273,9 @@ func TestUnexportedStructField(t *testing.T) {
badField bool
}
assert.NoError(t, ParseErr(&cmd, nil))
- assert.EqualError(t, ParseErr(&cmd, []string{"-badField"}), `unknown flag: "badField"`)
+ var ue userError
+ require.True(t, xerrors.As(ParseErr(&cmd, []string{"-badField"}), &ue))
+ assert.EqualValues(t, userError{`unknown flag: "badField"`}, ue)
}
func TestExcessArgsEmpty(t *testing.T) {
@@ -329,5 +321,46 @@ func TestSliceOfUnmarshallableStruct(t *testing.T) {
Complex []struct{}
}
require.EqualError(t, ParseErr(&cmd, []string{"herp"}), "can't marshal type struct {}")
+}
+func newStruct(ref interface{}) func() interface{} {
+ return func() interface{} {
+ return reflect.New(reflect.TypeOf(ref)).Interface()
+ }
+}
+
+func TestBasicPointer(t *testing.T) {
+ type cmd struct {
+ Maybe *bool
+ }
+ _true := true
+ RunCases(t, []parseCase{
+ noErrorCase(cmd{}),
+ errorCase(userError{`excess argument: "nope"`}, "nope"),
+ noErrorCase(cmd{Maybe: &_true}, "-maybe=true"),
+ }, newStruct(cmd{}))
+}
+
+func TestMarshalByteArray(t *testing.T) {
+ type cmd struct {
+ StartPos
+ Bs [2]byte
+ }
+ RunCases(t, []parseCase{
+ noErrorCase(cmd{Bs: func() (ret [2]byte) { copy(ret[:], "AB"); return }()}, "4142"),
+ anyErrorCase("41424"),
+ // anyErrorCase("4142"),
+ }, newStruct(cmd{}))
+}
+
+func TestMarshalStruct(t *testing.T) {
+ type InternalStruct struct {
+ A bool
+ }
+ var cmd struct {
+ Struct InternalStruct
+ StartPos
+ StructPos InternalStruct
+ }
+ ParseErr(&cmd, []string{"-struct", "structpos"})
}
diff --git a/usage.go b/usage.go
index 2597fcf..4754e80 100644
--- a/usage.go
+++ b/usage.go
@@ -5,14 +5,13 @@ import (
"io"
"text/tabwriter"
- "github.com/anacrolix/missinggo"
- "github.com/anacrolix/missinggo/slices"
+ "github.com/anacrolix/missinggo/v2"
+ "github.com/anacrolix/missinggo/v2/slices"
)
-func (p *parser) printUsage(w io.Writer) {
- fmt.Fprintf(w, "Usage:\n %s", p.program)
- if p.hasOptions() {
- fmt.Fprintf(w, " [OPTIONS...]")
+func (p *Parser) printPosArgUsage(w io.Writer) {
+ if p.parent != nil {
+ p.parent.printPosArgUsage(w)
}
for _, arg := range p.posArgs {
fs := func() string {
@@ -36,6 +35,14 @@ func (p *parser) printUsage(w io.Writer) {
// }
// }
}
+}
+
+func (p *Parser) printUsage(w io.Writer) {
+ fmt.Fprintf(w, "Usage:\n %s", p.program)
+ if p.hasOptions() {
+ fmt.Fprintf(w, " [OPTIONS...]")
+ }
+ p.printPosArgUsage(w)
fmt.Fprintf(w, "\n")
if p.description != "" {
fmt.Fprintf(w, "\n%s\n", missinggo.Unchomp(p.description))
@@ -73,12 +80,10 @@ func writeOptionUsage(w io.Writer, flags []arg) {
fmt.Fprintf(tw, "%s%s", flagPrefix, f.name)
help := f.help
if !f.hasZeroValue() {
- _default := fmt.Sprintf("Default: %v", f.value)
- if help == "" {
- help = _default
- } else {
- help = fmt.Sprintf("%s (%s)", help, _default)
+ if help != "" {
+ help += " "
}
+ help += fmt.Sprintf("(Default: %v)", f.value)
}
fmt.Fprintf(tw, "\t(%s)\t%s\n", f.value.Type(), help)
}
Debdiff
[The following lists of changes regard files as different if they have different names, permissions or owners.]
Files in second set of .debs but not in first
-rw-r--r-- root/root /usr/share/gocode/src/github.com/anacrolix/tagflag/cases_test.go -rw-r--r-- root/root /usr/share/gocode/src/github.com/anacrolix/tagflag/go.mod -rw-r--r-- root/root /usr/share/gocode/src/github.com/anacrolix/tagflag/go.sum
No differences were encountered in the control files