From df2442233fa136166c730bf63546cc684deed697 Mon Sep 17 00:00:00 2001 From: Pavel Sukhanov Date: Wed, 27 Jan 2021 20:44:25 +0300 Subject: [PATCH 1/6] Fix duplicated values for ... --- pattern.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pattern.go b/pattern.go index 0a29667..9695f19 100644 --- a/pattern.go +++ b/pattern.go @@ -284,6 +284,8 @@ func (p *pattern) match(left *patternList, collected *patternList) (bool, *patte times++ } if lAlt == l { + *left = *l + *collected = *c break } lAlt = l From 3015eb77c8520f22274add4e2526e60d56f793ba Mon Sep 17 00:00:00 2001 From: Pavel Sukhanov Date: Thu, 2 Sep 2021 12:54:03 +0300 Subject: [PATCH 2/6] replace imports in examples --- README.md | 8 ++++---- examples/arguments/arguments.go | 2 +- examples/arguments/arguments_test.go | 2 +- examples/calculator/calculator.go | 2 +- examples/calculator/calculator_test.go | 2 +- examples/config_file/config_file.go | 2 +- examples/counted/counted.go | 2 +- examples/counted/counted_test.go | 2 +- examples/examples.go | 2 +- examples/fake-git/branch/git_branch.go | 2 +- examples/fake-git/checkout/git_checkout.go | 2 +- examples/fake-git/clone/git_clone.go | 2 +- examples/fake-git/fakegit.go | 2 +- examples/fake-git/push/git_push.go | 2 +- examples/fake-git/remote/git_remote.go | 2 +- examples/naval_fate/naval_fate.go | 2 +- examples/odd_even/odd_even.go | 2 +- examples/options/options.go | 2 +- examples/options_shortcut/options_shortcut.go | 2 +- examples/quick/quick.go | 2 +- examples/type_assert/type_assert.go | 2 +- examples/unit_test/unit_test.go | 2 +- 22 files changed, 25 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index d03f8da..0ad7cf5 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ package main import ( "fmt" - "github.com/docopt/docopt-go" + "github.com/ps78674/docopt.go" ) func main() { @@ -47,13 +47,13 @@ Options: ⚠ Use the alias "docopt-go". To use docopt in your Go code: ```go -import "github.com/docopt/docopt-go" +import "github.com/ps78674/docopt.go" ``` To install docopt in your `$GOPATH`: ```console -$ go get github.com/docopt/docopt-go +$ go get github.com/ps78674/docopt.go ``` ## API @@ -103,7 +103,7 @@ var config struct { opts.Bind(&config) ``` -More documentation is available at [godoc.org](https://godoc.org/github.com/docopt/docopt-go). +More documentation is available at [godoc.org](https://godoc.org/github.com/ps78674/docopt.go). ## Unit Testing diff --git a/examples/arguments/arguments.go b/examples/arguments/arguments.go index 10074cb..cf4df14 100644 --- a/examples/arguments/arguments.go +++ b/examples/arguments/arguments.go @@ -2,7 +2,7 @@ package main import ( "fmt" - "github.com/docopt/docopt-go" + "github.com/ps78674/docopt.go" ) var usage = `Usage: arguments [-vqrh] [FILE] ... diff --git a/examples/arguments/arguments_test.go b/examples/arguments/arguments_test.go index e9a8654..35b54b0 100644 --- a/examples/arguments/arguments_test.go +++ b/examples/arguments/arguments_test.go @@ -1,7 +1,7 @@ package main import ( - "github.com/docopt/docopt-go/examples" + "github.com/ps78674/docopt.go/examples" ) func Example() { diff --git a/examples/calculator/calculator.go b/examples/calculator/calculator.go index 16939dc..ef3a3d1 100644 --- a/examples/calculator/calculator.go +++ b/examples/calculator/calculator.go @@ -2,7 +2,7 @@ package main import ( "fmt" - "github.com/docopt/docopt-go" + "github.com/ps78674/docopt.go" ) var usage = `Not a serious example. diff --git a/examples/calculator/calculator_test.go b/examples/calculator/calculator_test.go index eee1c13..1b5b2e5 100644 --- a/examples/calculator/calculator_test.go +++ b/examples/calculator/calculator_test.go @@ -1,7 +1,7 @@ package main import ( - "github.com/docopt/docopt-go/examples" + "github.com/ps78674/docopt.go/examples" ) func Example() { diff --git a/examples/config_file/config_file.go b/examples/config_file/config_file.go index bfa174c..c38930c 100644 --- a/examples/config_file/config_file.go +++ b/examples/config_file/config_file.go @@ -3,7 +3,7 @@ package main import ( "encoding/json" "fmt" - "github.com/docopt/docopt-go" + "github.com/ps78674/docopt.go" "strings" ) diff --git a/examples/counted/counted.go b/examples/counted/counted.go index c5d0c33..b8008e7 100644 --- a/examples/counted/counted.go +++ b/examples/counted/counted.go @@ -2,7 +2,7 @@ package main import ( "fmt" - "github.com/docopt/docopt-go" + "github.com/ps78674/docopt.go" ) var usage = `Usage: counted --help diff --git a/examples/counted/counted_test.go b/examples/counted/counted_test.go index 61f7cbc..a4bcb91 100644 --- a/examples/counted/counted_test.go +++ b/examples/counted/counted_test.go @@ -1,7 +1,7 @@ package main import ( - "github.com/docopt/docopt-go/examples" + "github.com/ps78674/docopt.go/examples" ) func Example() { diff --git a/examples/examples.go b/examples/examples.go index 180d79b..8ac7867 100644 --- a/examples/examples.go +++ b/examples/examples.go @@ -5,7 +5,7 @@ import ( "sort" "strings" - "github.com/docopt/docopt-go" + "github.com/ps78674/docopt.go" ) // TestUsage is a helper used to test the output from the examples in this folder. diff --git a/examples/fake-git/branch/git_branch.go b/examples/fake-git/branch/git_branch.go index b77beee..d020205 100644 --- a/examples/fake-git/branch/git_branch.go +++ b/examples/fake-git/branch/git_branch.go @@ -2,7 +2,7 @@ package main import ( "fmt" - "github.com/docopt/docopt-go" + "github.com/ps78674/docopt.go" ) func main() { diff --git a/examples/fake-git/checkout/git_checkout.go b/examples/fake-git/checkout/git_checkout.go index 0b9235c..63bea1c 100644 --- a/examples/fake-git/checkout/git_checkout.go +++ b/examples/fake-git/checkout/git_checkout.go @@ -2,7 +2,7 @@ package main import ( "fmt" - "github.com/docopt/docopt-go" + "github.com/ps78674/docopt.go" ) func main() { diff --git a/examples/fake-git/clone/git_clone.go b/examples/fake-git/clone/git_clone.go index 92adfcb..182af0d 100644 --- a/examples/fake-git/clone/git_clone.go +++ b/examples/fake-git/clone/git_clone.go @@ -2,7 +2,7 @@ package main import ( "fmt" - "github.com/docopt/docopt-go" + "github.com/ps78674/docopt.go" ) func main() { diff --git a/examples/fake-git/fakegit.go b/examples/fake-git/fakegit.go index 92e1ab5..f5356ff 100644 --- a/examples/fake-git/fakegit.go +++ b/examples/fake-git/fakegit.go @@ -2,7 +2,7 @@ package main import ( "fmt" - "github.com/docopt/docopt-go" + "github.com/ps78674/docopt.go" "os" "os/exec" ) diff --git a/examples/fake-git/push/git_push.go b/examples/fake-git/push/git_push.go index 2b47edc..6b9fd4a 100644 --- a/examples/fake-git/push/git_push.go +++ b/examples/fake-git/push/git_push.go @@ -2,7 +2,7 @@ package main import ( "fmt" - "github.com/docopt/docopt-go" + "github.com/ps78674/docopt.go" ) func main() { diff --git a/examples/fake-git/remote/git_remote.go b/examples/fake-git/remote/git_remote.go index c1d31e1..3ca038c 100644 --- a/examples/fake-git/remote/git_remote.go +++ b/examples/fake-git/remote/git_remote.go @@ -2,7 +2,7 @@ package main import ( "fmt" - "github.com/docopt/docopt-go" + "github.com/ps78674/docopt.go" ) func main() { diff --git a/examples/naval_fate/naval_fate.go b/examples/naval_fate/naval_fate.go index 4827d44..0cb2a7b 100644 --- a/examples/naval_fate/naval_fate.go +++ b/examples/naval_fate/naval_fate.go @@ -2,7 +2,7 @@ package main import ( "fmt" - "github.com/docopt/docopt-go" + "github.com/ps78674/docopt.go" ) func main() { diff --git a/examples/odd_even/odd_even.go b/examples/odd_even/odd_even.go index 1d9e617..61358dc 100644 --- a/examples/odd_even/odd_even.go +++ b/examples/odd_even/odd_even.go @@ -2,7 +2,7 @@ package main import ( "fmt" - "github.com/docopt/docopt-go" + "github.com/ps78674/docopt.go" ) func main() { diff --git a/examples/options/options.go b/examples/options/options.go index b3c3398..b4589ec 100644 --- a/examples/options/options.go +++ b/examples/options/options.go @@ -2,7 +2,7 @@ package main import ( "fmt" - "github.com/docopt/docopt-go" + "github.com/ps78674/docopt.go" ) func main() { diff --git a/examples/options_shortcut/options_shortcut.go b/examples/options_shortcut/options_shortcut.go index 6dbd394..31f2212 100644 --- a/examples/options_shortcut/options_shortcut.go +++ b/examples/options_shortcut/options_shortcut.go @@ -2,7 +2,7 @@ package main import ( "fmt" - "github.com/docopt/docopt-go" + "github.com/ps78674/docopt.go" ) func main() { diff --git a/examples/quick/quick.go b/examples/quick/quick.go index 63835a1..ebdc330 100644 --- a/examples/quick/quick.go +++ b/examples/quick/quick.go @@ -2,7 +2,7 @@ package main import ( "fmt" - "github.com/docopt/docopt-go" + "github.com/ps78674/docopt.go" ) func main() { diff --git a/examples/type_assert/type_assert.go b/examples/type_assert/type_assert.go index 61c2c4e..442ee4c 100644 --- a/examples/type_assert/type_assert.go +++ b/examples/type_assert/type_assert.go @@ -2,7 +2,7 @@ package main import ( "fmt" - "github.com/docopt/docopt-go" + "github.com/ps78674/docopt.go" ) func main() { diff --git a/examples/unit_test/unit_test.go b/examples/unit_test/unit_test.go index 18428d9..a0250fb 100644 --- a/examples/unit_test/unit_test.go +++ b/examples/unit_test/unit_test.go @@ -1,7 +1,7 @@ package main import ( - "github.com/docopt/docopt-go" + "github.com/ps78674/docopt.go" "reflect" "testing" ) From 3c49232c8128212d034501c77f55f3b94c70f463 Mon Sep 17 00:00:00 2001 From: Pavel Sukhanov Date: Thu, 2 Sep 2021 12:54:34 +0300 Subject: [PATCH 3/6] add go.mod --- go.mod | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 go.mod diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..6af7f1f --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/ps78674/docopt.go + +go 1.17 From 780177a6d90890afbc42955119428f758b7b179d Mon Sep 17 00:00:00 2001 From: Pavel Sukhanov Date: Mon, 24 Jan 2022 17:42:29 +0300 Subject: [PATCH 4/6] allow use of non-zero struct fields for bind --- opts.go | 11 ++++++----- opts_test.go | 23 ++++++++++++----------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/opts.go b/opts.go index 36320fb..bcfe487 100644 --- a/opts.go +++ b/opts.go @@ -162,11 +162,12 @@ func (o Opts) Bind(v interface{}) error { } return newError("mapping of %q is not found in given struct, or is an unexported field", k) } - fieldVal := structVal.Field(i) - zeroVal := reflect.Zero(fieldVal.Type()) - if !reflect.DeepEqual(fieldVal.Interface(), zeroVal.Interface()) { - return newError("%q field is non-zero, will be overwritten by value of %q", structType.Field(i).Name, k) - } + // Allow use of non-zero struct fields for bind + // fieldVal := structVal.Field(i) + // zeroVal := reflect.Zero(fieldVal.Type()) + // if !reflect.DeepEqual(fieldVal.Interface(), zeroVal.Interface()) { + // return newError("%q field is non-zero, will be overwritten by value of %q", structType.Field(i).Name, k) + // } indexMap[k] = i } diff --git a/opts_test.go b/opts_test.go index 3c5c480..8945f52 100644 --- a/opts_test.go +++ b/opts_test.go @@ -273,17 +273,18 @@ func TestBindSimpleStruct(t *testing.T) { } } -func TestBindToStructWhichAlreadyHasValue(t *testing.T) { - var testParser = &Parser{HelpHandler: NoHelpHandler, SkipHelpFlags: true} - opts, err := testParser.ParseArgs("Usage: prog [--number=X]", []string{"--number=123"}, "") - if err != nil { - t.Fatal(err) - } - var opt = struct{ Number int }{1} - if err := opts.Bind(&opt); err == nil { - t.Fatal("error expected") - } -} +// Allow use of non-zero struct fields for bind +// func TestBindToStructWhichAlreadyHasValue(t *testing.T) { +// var testParser = &Parser{HelpHandler: NoHelpHandler, SkipHelpFlags: true} +// opts, err := testParser.ParseArgs("Usage: prog [--number=X]", []string{"--number=123"}, "") +// if err != nil { +// t.Fatal(err) +// } +// var opt = struct{ Number int }{1} +// if err := opts.Bind(&opt); err == nil { +// t.Fatal("error expected") +// } +// } func TestBindDashTag(t *testing.T) { var testParser = &Parser{HelpHandler: NoHelpHandler, SkipHelpFlags: true} From dc5d095e2481bd50934ca84b2aa18c27e25c1ce0 Mon Sep 17 00:00:00 2001 From: Pavel Sukhanov Date: Mon, 24 Jan 2022 17:48:58 +0300 Subject: [PATCH 5/6] add support for time.Duration --- opts.go | 10 ++++++++++ opts_test.go | 20 +++++++++++++------- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/opts.go b/opts.go index bcfe487..958c8d9 100644 --- a/opts.go +++ b/opts.go @@ -5,6 +5,7 @@ import ( "reflect" "strconv" "strings" + "time" "unicode" ) @@ -200,6 +201,15 @@ func (o Opts) Bind(v interface{}) error { // Try to convert the value and assign if able. switch field.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + ft := field.Type() + if ft.PkgPath() == "time" && ft.Name() == "Duration" { + if s, err := o.String(k); err == nil { + if d, err := time.ParseDuration(s); err == nil { + field.SetInt(int64(d)) + continue + } + } + } if x, err := o.Int(k); err == nil { field.SetInt(int64(x)) continue diff --git a/opts_test.go b/opts_test.go index 8945f52..954e1b6 100644 --- a/opts_test.go +++ b/opts_test.go @@ -4,6 +4,7 @@ import ( "reflect" "strings" "testing" + "time" ) func TestOptsUsage(t *testing.T) { @@ -141,13 +142,14 @@ func TestOptsBind(t *testing.T) { type testTypedOptions struct { secret int `docopt:"-s"` - V bool - Number int16 - Idle float32 - Pointer uintptr `docopt:""` - Ints []int `docopt:""` - Strings []string `docopt:"STRINGS"` - Iface interface{} `docopt:"IFACE"` + V bool + Number int16 + Idle float32 + Pointer uintptr `docopt:""` + Ints []int `docopt:""` + Strings []string `docopt:"STRINGS"` + Iface interface{} `docopt:"IFACE"` + Duration time.Duration } func TestBindErrors(t *testing.T) { @@ -244,6 +246,10 @@ func TestBindSuccess(t *testing.T) { `Usage: prog [--help]`, `prog --help`, }, + { + `Usage: prog `, + `prog 10s`, + }, } { argv := strings.Split(tc.command, " ")[1:] opts, err := testParser.ParseArgs(tc.usage, argv, "") From c5b609ecf7382efc6d6cc8238ea8c9706e5d9009 Mon Sep 17 00:00:00 2001 From: Pavel Sukhanov Date: Mon, 24 Jan 2022 18:31:10 +0300 Subject: [PATCH 6/6] add env support (default <- env <- argv) --- README.md | 2 +- docopt.go | 15 +++++++++++---- docopt_test.go | 9 +++++++++ 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 0ad7cf5..f3b2a0a 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Usage: Options: -h --help Show this screen. --version Show version. - --speed= Speed in knots [default: 10]. + --speed= Speed in knots [default: 10, env: SPEED]. --moored Moored (anchored) mine. --drifting Drifting mine.` diff --git a/docopt.go b/docopt.go index c22feb7..bfdd071 100644 --- a/docopt.go +++ b/docopt.go @@ -287,7 +287,8 @@ func parseOption(optionDescription string) *pattern { var value interface{} value = false - reDefault := regexp.MustCompile(`(?i)\[default: (.*)\]`) + reDefault := regexp.MustCompile(`(?i)\[.*default: ([^,]*).*[\]]`) + reEnv := regexp.MustCompile(`(?i)\[.*env: ([^,]*).*[\]]`) for _, s := range strings.Fields(options) { if strings.HasPrefix(s, "--") { long = s @@ -297,10 +298,16 @@ func parseOption(optionDescription string) *pattern { argcount = 1 } if argcount > 0 { - matched := reDefault.FindAllStringSubmatch(description, -1) - if len(matched) > 0 { + if matched := reDefault.FindAllStringSubmatch(description, -1); len(matched) > 0 { value = matched[0][1] - } else { + } + if matched := reEnv.FindAllStringSubmatch(description, -1); len(matched) > 0 { + v, ok := os.LookupEnv(matched[0][1]) + if ok { + value = v + } + } + if value == false { value = nil } } diff --git a/docopt_test.go b/docopt_test.go index 2439d74..9ce4e7c 100644 --- a/docopt_test.go +++ b/docopt_test.go @@ -1267,6 +1267,15 @@ func TestDefaultValueForPositionalArguments(t *testing.T) { } } +func TestEnvValueForPositionalArguments(t *testing.T) { + os.Setenv("DATA", "a b") + defer os.Unsetenv("DATA") + doc := "Usage: prog [--data=...]\nOptions:\n\t-d --data= Input data [env: DATA]" + if v, err := testParser.ParseArgs(doc, []string{}, ""); reflect.DeepEqual(v, Opts{"--data": []string{"a", "b"}}) != true { + t.Error(err) + } +} + func TestIssue59(t *testing.T) { if v, err := testParser.ParseArgs("usage: prog --long=", []string{"--long="}, ""); reflect.DeepEqual(v, Opts{"--long": ""}) != true { t.Error(err)