From e8e1a4700ed4620b6af825f900951737c3b45564 Mon Sep 17 00:00:00 2001 From: Tim Otlik Date: Fri, 28 Jun 2019 23:21:28 +0200 Subject: [PATCH 1/4] refactor: remove code from v2 for tagging of v1 --- go.mod | 3 + v2/bool.go | 36 --- v2/bool_test.go | 106 -------- v2/breakup_test.go | 34 --- v2/counter.go | 69 ----- v2/counter_test.go | 76 ------ v2/duration.go | 28 --- v2/duration_test.go | 73 ------ v2/enum.go | 79 ------ v2/enum_test.go | 79 ------ v2/error.go | 93 ------- v2/generic.go | 247 ------------------ v2/generic_test.go | 319 ------------------------ v2/getopt.go | 564 ----------------------------------------- v2/help_test.go | 145 ----------- v2/int.go | 157 ------------ v2/int_test.go | 595 -------------------------------------------- v2/list.go | 32 --- v2/list_test.go | 99 -------- v2/option.go | 212 ---------------- v2/set.go | 293 ---------------------- v2/signed.go | 110 -------- v2/signed_test.go | 97 -------- v2/string.go | 27 -- v2/string_test.go | 77 ------ v2/unsigned.go | 111 --------- v2/unsigned_test.go | 97 -------- v2/util_test.go | 85 ------- v2/var.go | 100 -------- 29 files changed, 3 insertions(+), 4040 deletions(-) create mode 100644 go.mod delete mode 100644 v2/bool.go delete mode 100644 v2/bool_test.go delete mode 100644 v2/breakup_test.go delete mode 100644 v2/counter.go delete mode 100644 v2/counter_test.go delete mode 100644 v2/duration.go delete mode 100644 v2/duration_test.go delete mode 100644 v2/enum.go delete mode 100644 v2/enum_test.go delete mode 100644 v2/error.go delete mode 100644 v2/generic.go delete mode 100644 v2/generic_test.go delete mode 100644 v2/getopt.go delete mode 100644 v2/help_test.go delete mode 100644 v2/int.go delete mode 100644 v2/int_test.go delete mode 100644 v2/list.go delete mode 100644 v2/list_test.go delete mode 100644 v2/option.go delete mode 100644 v2/set.go delete mode 100644 v2/signed.go delete mode 100644 v2/signed_test.go delete mode 100644 v2/string.go delete mode 100644 v2/string_test.go delete mode 100644 v2/unsigned.go delete mode 100644 v2/unsigned_test.go delete mode 100644 v2/util_test.go delete mode 100644 v2/var.go diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..66c9762 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/sambadevi/getopt + +go 1.12 diff --git a/v2/bool.go b/v2/bool.go deleted file mode 100644 index 2899bee..0000000 --- a/v2/bool.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2017 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -// Bool creates a flag option that is a bool. Bools normally do not take a -// value however one can be assigned by using the long form of the option: -// -// --option=true -// --o=false -// -// The value is case insensitive and one of true, false, t, f, on, off, t and 0. -func Bool(name rune, helpvalue ...string) *bool { - var b bool - CommandLine.Flag(&b, name, helpvalue...) - return &b -} - -func BoolLong(name string, short rune, helpvalue ...string) *bool { - var p bool - CommandLine.FlagLong(&p, name, short, helpvalue...) - return &p -} - -func (s *Set) Bool(name rune, helpvalue ...string) *bool { - var b bool - s.Flag(&b, name, helpvalue...) - return &b -} - -func (s *Set) BoolLong(name string, short rune, helpvalue ...string) *bool { - var p bool - s.FlagLong(&p, name, short, helpvalue...) - return &p -} diff --git a/v2/bool_test.go b/v2/bool_test.go deleted file mode 100644 index 0a90d96..0000000 --- a/v2/bool_test.go +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2017 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -import ( - "fmt" - "strings" - "testing" -) - -var boolTests = []struct { - where string - in []string - f bool - fc int - opt bool - optc int - err string -}{ - { - loc(), - []string{}, - false, 0, - false, 0, - "", - }, - { - loc(), - []string{"test", "-f", "--opt"}, - true, 1, - true, 1, - "", - }, - { - loc(), - []string{"test", "--f", "--opt"}, - true, 1, - true, 1, - "", - }, - { - loc(), - []string{"test", "-ff", "-f", "--opt", "--opt"}, - true, 3, - true, 2, - "", - }, - { - loc(), - []string{"test", "--opt", "--opt=false"}, - false, 0, - false, 2, - "", - }, - { - loc(), - []string{"test", "-f", "false"}, - true, 1, - false, 0, - "", - }, - { - loc(), - []string{"test", "-f=false"}, - true, 1, - false, 0, - "test: unknown option: -=\n", - }, - { - loc(), - []string{"test", "-f", "false"}, - true, 1, - false, 0, - "", - }, -} - -func TestBool(t *testing.T) { - for x, tt := range boolTests { - reset() - f := Bool('f') - opt := BoolLong("opt", 0) - if strings.Index(tt.where, ":-") > 0 { - tt.where = fmt.Sprintf("#%d", x) - } - - parse(tt.in) - if s := checkError(tt.err); s != "" { - t.Errorf("%s: %s", tt.where, s) - } - if got, want := *f, tt.f; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - if got, want := *opt, tt.opt; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - if got, want := GetCount('f'), tt.fc; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - if got, want := GetCount("opt"), tt.optc; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - } -} diff --git a/v2/breakup_test.go b/v2/breakup_test.go deleted file mode 100644 index 5609e50..0000000 --- a/v2/breakup_test.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2017 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -import ( - "testing" -) - -var breakupTests = []struct { - in string - max int - out []string -}{ - {"", 8, []string{}}, - {"a fox", 8, []string{"a fox"}}, - {"a foxhound is sly", 2, []string{"a", "foxhound", "is", "sly"}}, - {"a foxhound is sly", 5, []string{"a", "foxhound", "is", "sly"}}, - {"a foxhound is sly", 6, []string{"a", "foxhound", "is sly"}}, - {"a foxhound is sly", 7, []string{"a", "foxhound", "is sly"}}, - {"a foxhound is sly", 8, []string{"a", "foxhound", "is sly"}}, - {"a foxhound is sly", 9, []string{"a", "foxhound", "is sly"}}, - {"a foxhound is sly", 10, []string{"a foxhound", "is sly"}}, -} - -func TestBreakup(t *testing.T) { - for x, tt := range breakupTests { - out := breakup(tt.in, tt.max) - if badSlice(out, tt.out) { - t.Errorf("#%d: got %v, want %v", x, out, tt.out) - } - } -} diff --git a/v2/counter.go b/v2/counter.go deleted file mode 100644 index ab10bc7..0000000 --- a/v2/counter.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2017 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -import ( - "fmt" - "strconv" -) - -type counterValue int - -func (b *counterValue) Set(value string, opt Option) error { - if value == "" { - *b++ - } else { - v, err := strconv.ParseInt(value, 0, strconv.IntSize) - if err != nil { - if e, ok := err.(*strconv.NumError); ok { - switch e.Err { - case strconv.ErrRange: - err = fmt.Errorf("value out of range: %s", value) - case strconv.ErrSyntax: - err = fmt.Errorf("not a valid number: %s", value) - } - } - return err - } - *b = counterValue(v) - } - return nil -} - -func (b *counterValue) String() string { - return strconv.Itoa(int(*b)) -} - -// Counter creates a counting flag stored as an int. Each time the option -// is seen while parsing the value is incremented. The value of the counter -// may be explicitly set by using the long form: -// -// --counter=5 -// --c=5 -// -// Further instances of the option will increment from the set value. -func Counter(name rune, helpvalue ...string) *int { - var p int - CommandLine.FlagLong((*counterValue)(&p), "", name, helpvalue...).SetFlag() - return &p -} - -func (s *Set) Counter(name rune, helpvalue ...string) *int { - var p int - s.FlagLong((*counterValue)(&p), "", name, helpvalue...).SetFlag() - return &p -} - -func CounterLong(name string, short rune, helpvalue ...string) *int { - var p int - CommandLine.FlagLong((*counterValue)(&p), name, short, helpvalue...).SetFlag() - return &p -} - -func (s *Set) CounterLong(name string, short rune, helpvalue ...string) *int { - var p int - s.FlagLong((*counterValue)(&p), name, short, helpvalue...).SetFlag() - return &p -} diff --git a/v2/counter_test.go b/v2/counter_test.go deleted file mode 100644 index b85a26d..0000000 --- a/v2/counter_test.go +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2017 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -import ( - "fmt" - "strings" - "testing" -) - -var counterTests = []struct { - where string - in []string - c int - cnt int - err string -}{ - { - loc(), - []string{}, - 0, - 0, - "", - }, - { - loc(), - []string{"test", "-c", "--cnt"}, - 1, - 1, - "", - }, - { - loc(), - []string{"test", "-cc", "-c", "--cnt", "--cnt"}, - 3, - 2, - "", - }, - { - loc(), - []string{"test", "--c=17", "--cnt=42"}, - 17, - 42, - "", - }, - { - loc(), - []string{"test", "--cnt=false"}, - 0, 0, - "test: not a valid number: false\n", - }, -} - -func TestCounter(t *testing.T) { - for x, tt := range counterTests { - reset() - c := Counter('c') - cnt := CounterLong("cnt", 0) - if strings.Index(tt.where, ":-") > 0 { - tt.where = fmt.Sprintf("#%d", x) - } - - parse(tt.in) - if s := checkError(tt.err); s != "" { - t.Errorf("%s: %s", tt.where, s) - } - if got, want := *c, tt.c; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - if got, want := *cnt, tt.cnt; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - } -} diff --git a/v2/duration.go b/v2/duration.go deleted file mode 100644 index 7607815..0000000 --- a/v2/duration.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2017 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -import "time" - -// Duration creates an option that parses its value as a time.Duration. -func Duration(name rune, value time.Duration, helpvalue ...string) *time.Duration { - CommandLine.FlagLong(&value, "", name, helpvalue...) - return &value -} - -func (s *Set) Duration(name rune, value time.Duration, helpvalue ...string) *time.Duration { - s.FlagLong(&value, "", name, helpvalue...) - return &value -} - -func DurationLong(name string, short rune, value time.Duration, helpvalue ...string) *time.Duration { - CommandLine.FlagLong(&value, name, short, helpvalue...) - return &value -} - -func (s *Set) DurationLong(name string, short rune, value time.Duration, helpvalue ...string) *time.Duration { - s.FlagLong(&value, name, short, helpvalue...) - return &value -} diff --git a/v2/duration_test.go b/v2/duration_test.go deleted file mode 100644 index ffee177..0000000 --- a/v2/duration_test.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2017 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -import ( - "fmt" - "strings" - "testing" - "time" -) - -var durationTests = []struct { - where string - in []string - d time.Duration - dur time.Duration - err string -}{ - { - loc(), - []string{}, - 17, 42, - "", - }, - { - loc(), - []string{"test", "-d", "1s", "--duration", "2s"}, - time.Second, 2 * time.Second, - "", - }, - { - loc(), - []string{"test", "-d1s", "-d2s"}, - 2 * time.Second, 42, - "", - }, - { - loc(), - []string{"test", "-d1"}, - 17, 42, - "test: time: missing unit in duration 1\n", - }, - { - loc(), - []string{"test", "--duration", "foo"}, - 17, 42, - "test: time: invalid duration foo\n", - }, -} - -func TestDuration(t *testing.T) { - for x, tt := range durationTests { - reset() - d := Duration('d', 17) - opt := DurationLong("duration", 0, 42) - if strings.Index(tt.where, ":-") > 0 { - tt.where = fmt.Sprintf("#%d", x) - } - - parse(tt.in) - if s := checkError(tt.err); s != "" { - t.Errorf("%s: %s", tt.where, s) - } - if got, want := *d, tt.d; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - if got, want := *opt, tt.dur; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - } -} diff --git a/v2/enum.go b/v2/enum.go deleted file mode 100644 index 1ca6ff9..0000000 --- a/v2/enum.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2017 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -import ( - "errors" - "fmt" - "sync" -) - -type enumValue string - -var ( - enumValuesMu sync.Mutex - enumValues = make(map[*enumValue]map[string]struct{}) -) - -func (s *enumValue) Set(value string, opt Option) error { - enumValuesMu.Lock() - es, ok := enumValues[s] - enumValuesMu.Unlock() - if !ok || es == nil { - return errors.New("this option has no values") - } - if _, ok := es[value]; !ok { - return errors.New("invalid value: " + value) - } - *s = enumValue(value) - return nil -} - -func (s *enumValue) String() string { - return string(*s) -} - -// Enum creates an option that can only be set to one of the enumerated strings -// passed in values. Passing nil or an empty slice results in an option that -// will always fail. If not "", value is the default value of the enum. If -// value is not listed in values then Enum will produce an error on standard -// error and then exit the program with a status of 1. -func Enum(name rune, values []string, value string, helpvalue ...string) *string { - return CommandLine.Enum(name, values, value, helpvalue...) -} - -func (s *Set) Enum(name rune, values []string, value string, helpvalue ...string) *string { - var p enumValue - p.define(values, value, &option{short: name}) - s.FlagLong(&p, "", name, helpvalue...) - return (*string)(&p) -} - -func EnumLong(name string, short rune, values []string, value string, helpvalue ...string) *string { - return CommandLine.EnumLong(name, short, values, value, helpvalue...) -} - -func (s *Set) EnumLong(name string, short rune, values []string, value string, helpvalue ...string) *string { - var p enumValue - p.define(values, value, &option{short: short, long: name}) - s.FlagLong(&p, name, short, helpvalue...) - return (*string)(&p) -} - -func (e *enumValue) define(values []string, def string, opt Option) { - m := make(map[string]struct{}) - for _, v := range values { - m[v] = struct{}{} - } - enumValuesMu.Lock() - enumValues[e] = m - enumValuesMu.Unlock() - if def != "" { - if err := e.Set(def, nil); err != nil { - fmt.Fprintf(stderr, "setting default for %s: %v\n", opt.Name(), err) - exit(1) - } - } -} diff --git a/v2/enum_test.go b/v2/enum_test.go deleted file mode 100644 index c854015..0000000 --- a/v2/enum_test.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2017 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -import ( - "fmt" - "strings" - "testing" -) - -var enumTests = []struct { - where string - in []string - values []string - def string - out string - err string -}{ - { - loc(), - nil, - []string{}, - "", - "", - "", - }, - { - loc(), - []string{"test", "-e", "val1"}, - []string{"val1", "val2"}, - "", - "val1", - "", - }, - { - loc(), - []string{"test", "-e", "val1", "-e", "val2"}, - []string{"val1", "val2"}, - "", - "val2", - "", - }, - { - loc(), - []string{"test"}, - []string{"val1", "val2"}, - "val2", - "val2", - "", - }, - { - loc(), - []string{"test", "-e", "val3"}, - []string{"val1", "val2"}, - "", - "", - "test: invalid value: val3\n", - }, -} - -func TestEnum(t *testing.T) { - for x, tt := range enumTests { - if strings.Index(tt.where, ":-") > 0 { - tt.where = fmt.Sprintf("#%d", x) - } - - reset() - e := Enum('e', tt.values, tt.def) - parse(tt.in) - if s := checkError(tt.err); s != "" { - t.Errorf("%s: %s", tt.where, s) - } - if *e != tt.out { - t.Errorf("%s: got %v, want %v", tt.where, *e, tt.out) - } - } -} diff --git a/v2/error.go b/v2/error.go deleted file mode 100644 index 6f668a3..0000000 --- a/v2/error.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2017 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -import "fmt" - -// An Error is returned by Getopt when it encounters an error. -type Error struct { - ErrorCode // General reason of failure. - Err error // The actual error. - Parameter string // Parameter passed to option, if any - Name string // Option that cause error, if any -} - -// Error returns the error message, implementing the error interface. -func (i *Error) Error() string { return i.Err.Error() } - -// An ErrorCode indicates what sort of error was encountered. -type ErrorCode int - -const ( - NoError = ErrorCode(iota) - UnknownOption // an invalid option was encountered - MissingParameter // the options parameter is missing - ExtraParameter // a value was set to a long flag - Invalid // attempt to set an invalid value -) - -func (e ErrorCode) String() string { - switch e { - case UnknownOption: - return "unknow option" - case MissingParameter: - return "missing argument" - case ExtraParameter: - return "unxpected value" - case Invalid: - return "error setting value" - } - return "unknown error" -} - -// unknownOption returns an Error indicating an unknown option was -// encountered. -func unknownOption(name interface{}) *Error { - i := &Error{ErrorCode: UnknownOption} - switch n := name.(type) { - case rune: - if n == '-' { - i.Name = "-" - } else { - i.Name = "-" + string(n) - } - case string: - i.Name = "--" + n - } - i.Err = fmt.Errorf("unknown option: %s", i.Name) - return i -} - -// missingArg returns an Error inidicating option o was not passed -// a required paramter. -func missingArg(o Option) *Error { - return &Error{ - ErrorCode: MissingParameter, - Name: o.Name(), - Err: fmt.Errorf("missing parameter for %s", o.Name()), - } -} - -// extraArg returns an Error inidicating option o was passed the -// unexpected paramter value. -func extraArg(o Option, value string) *Error { - return &Error{ - ErrorCode: ExtraParameter, - Name: o.Name(), - Parameter: value, - Err: fmt.Errorf("unexpected parameter passed to %s: %q", o.Name(), value), - } -} - -// setError returns an Error inidicating option o and the specified -// error while setting it to value. -func setError(o Option, value string, err error) *Error { - return &Error{ - ErrorCode: Invalid, - Name: o.Name(), - Parameter: value, - Err: err, - } -} diff --git a/v2/generic.go b/v2/generic.go deleted file mode 100644 index 2cead19..0000000 --- a/v2/generic.go +++ /dev/null @@ -1,247 +0,0 @@ -// Copyright 2017 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -import ( - "fmt" - "strconv" - "strings" - "time" -) - -type generic struct { - p interface{} -} - -// Flag is shorthand for CommandLine.Flag. -func Flag(v interface{}, short rune, helpvalue ...string) Option { - return CommandLine.long(v, "", short, helpvalue...) -} - -// FlagLong is shorthand for CommandLine.LongFlag. -func FlagLong(v interface{}, long string, short rune, helpvalue ...string) Option { - return CommandLine.long(v, long, short, helpvalue...) -} - -// Flag calls FlagLong with only a short flag name. -func (s *Set) Flag(v interface{}, short rune, helpvalue ...string) Option { - return s.long(v, "", short, helpvalue...) -} - -// FlagLong returns an Option in Set s for setting v. If long is not "" then -// the option has a long name, and if short is not 0, the option has a short -// name. v must either be of type getopt.Value or a pointer to one of the -// supported builtin types: -// -// bool, string, []string -// int, int8, int16, int32, int64 -// uint, uint8, uint16, uint32, uint64 -// float32, float64 -// time.Duration -// -// FlagLong will panic if v is not a getopt.Value or one of the supported -// builtin types. -// -// The default value of the flag is the value of v at the time FlagLong is -// called. -func (s *Set) FlagLong(v interface{}, long string, short rune, helpvalue ...string) Option { - return s.long(v, long, short, helpvalue...) -} - -func (s *Set) long(v interface{}, long string, short rune, helpvalue ...string) (opt Option) { - // Fix up our location when we return. - if where := calledFrom(); where != "" { - defer func() { - if opt, ok := opt.(*option); ok { - opt.where = where - } - }() - } - switch p := v.(type) { - case Value: - return s.addFlag(p, long, short, helpvalue...) - case *bool: - return s.addFlag(&generic{v}, long, short, helpvalue...).SetFlag() - case *string, *[]string: - return s.addFlag(&generic{v}, long, short, helpvalue...) - case *int, *int8, *int16, *int32, *int64: - return s.addFlag(&generic{v}, long, short, helpvalue...) - case *uint, *uint8, *uint16, *uint32, *uint64: - return s.addFlag(&generic{v}, long, short, helpvalue...) - case *float32, *float64: - return s.addFlag(&generic{v}, long, short, helpvalue...) - case *time.Duration: - return s.addFlag(&generic{v}, long, short, helpvalue...) - default: - panic(fmt.Sprintf("unsupported flag type: %T", v)) - } -} - -// genericValue returns the object underlying the generic Value v, or nil if v -// is not a generic Value. -func genericValue(v Value) interface{} { - if g, ok := v.(*generic); ok { - return g.p - } - return nil -} - -func (g *generic) Set(value string, opt Option) error { - strconvErr := func(err error) error { - if e, ok := err.(*strconv.NumError); ok { - switch e.Err { - case strconv.ErrRange: - err = fmt.Errorf("value out of range: %s", value) - case strconv.ErrSyntax: - err = fmt.Errorf("not a valid number: %s", value) - } - } - return err - } - switch p := g.p.(type) { - case *bool: - switch strings.ToLower(value) { - case "", "1", "true", "on", "t": - *p = true - case "0", "false", "off", "f": - *p = false - default: - return fmt.Errorf("invalid value for bool %s: %q", opt.Name(), value) - } - return nil - case *string: - *p = value - return nil - case *[]string: - a := strings.Split(value, ",") - // If this is the first time we are seen then nil out the - // default value. - if opt.Count() <= 1 { - *p = nil - } - *p = append(*p, a...) - return nil - case *int: - i64, err := strconv.ParseInt(value, 0, strconv.IntSize) - if err == nil { - *p = int(i64) - } - return strconvErr(err) - case *int8: - i64, err := strconv.ParseInt(value, 0, 8) - if err == nil { - *p = int8(i64) - } - return strconvErr(err) - case *int16: - i64, err := strconv.ParseInt(value, 0, 16) - if err == nil { - *p = int16(i64) - } - return strconvErr(err) - case *int32: - i64, err := strconv.ParseInt(value, 0, 32) - if err == nil { - *p = int32(i64) - } - return strconvErr(err) - case *int64: - i64, err := strconv.ParseInt(value, 0, 64) - if err == nil { - *p = i64 - } - return strconvErr(err) - case *uint: - u64, err := strconv.ParseUint(value, 0, strconv.IntSize) - if err == nil { - *p = uint(u64) - } - return strconvErr(err) - case *uint8: - u64, err := strconv.ParseUint(value, 0, 8) - if err == nil { - *p = uint8(u64) - } - return strconvErr(err) - case *uint16: - u64, err := strconv.ParseUint(value, 0, 16) - if err == nil { - *p = uint16(u64) - } - return strconvErr(err) - case *uint32: - u64, err := strconv.ParseUint(value, 0, 32) - if err == nil { - *p = uint32(u64) - } - return strconvErr(err) - case *uint64: - u64, err := strconv.ParseUint(value, 0, 64) - if err == nil { - *p = u64 - } - return strconvErr(err) - case *float32: - f64, err := strconv.ParseFloat(value, 32) - if err == nil { - *p = float32(f64) - } - return strconvErr(err) - case *float64: - f64, err := strconv.ParseFloat(value, 64) - if err == nil { - *p = f64 - } - return strconvErr(err) - case *time.Duration: - v, err := time.ParseDuration(value) - if err == nil { - *p = v - } - return err - } - panic("internal error") -} - -func (g *generic) String() string { - switch p := g.p.(type) { - case *bool: - if *p { - return "true" - } - return "false" - case *string: - return *p - case *[]string: - return strings.Join([]string(*p), ",") - case *int: - return strconv.FormatInt(int64(*p), 10) - case *int8: - return strconv.FormatInt(int64(*p), 10) - case *int16: - return strconv.FormatInt(int64(*p), 10) - case *int32: - return strconv.FormatInt(int64(*p), 10) - case *int64: - return strconv.FormatInt(*p, 10) - case *uint: - return strconv.FormatUint(uint64(*p), 10) - case *uint8: - return strconv.FormatUint(uint64(*p), 10) - case *uint16: - return strconv.FormatUint(uint64(*p), 10) - case *uint32: - return strconv.FormatUint(uint64(*p), 10) - case *uint64: - return strconv.FormatUint(*p, 10) - case *float32: - return strconv.FormatFloat(float64(*p), 'g', -1, 32) - case *float64: - return strconv.FormatFloat(*p, 'g', -1, 64) - case *time.Duration: - return p.String() - } - panic("internal error") -} diff --git a/v2/generic_test.go b/v2/generic_test.go deleted file mode 100644 index 9a247ec..0000000 --- a/v2/generic_test.go +++ /dev/null @@ -1,319 +0,0 @@ -// Copyright 2017 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -import ( - "bytes" - "fmt" - "os" - "reflect" - "runtime" - "strings" - "testing" - "time" -) - -func TestGeneric(t *testing.T) { - const ( - shortTest = iota - longTest - bothTest - ) - for _, tt := range []struct { - where string - kind int - val interface{} - str string - def interface{} - in []string - err string - }{ - // Do all four tests for string, the rest can mostly just use - // shortTest (the 0 value). - { - where: loc(), - kind: shortTest, - val: "42", - str: "42", - in: []string{"test", "-s", "42"}, - }, - { - where: loc(), - kind: longTest, - val: "42", - str: "42", - in: []string{"test", "--long", "42"}, - }, - { - where: loc(), - kind: bothTest, - val: "42", - str: "42", - in: []string{"test", "--both", "42"}, - }, - { - where: loc(), - kind: bothTest, - val: "42", - str: "42", - in: []string{"test", "-b", "42"}, - }, - { - where: loc(), - val: "42", - def: "42", - str: "42", - in: []string{"test"}, - }, - { - where: loc(), - val: "42", - def: "43", - str: "42", - in: []string{"test", "-s", "42"}, - }, - - { - where: loc(), - val: true, - str: "true", - in: []string{"test", "-s"}, - }, - { - where: loc(), - val: true, - def: true, - str: "true", - in: []string{"test"}, - }, - { - where: loc(), - kind: longTest, - val: false, - str: "false", - in: []string{"test", "--long=false"}, - }, - { - where: loc(), - kind: longTest, - val: false, - def: true, - str: "false", - in: []string{"test", "--long=false"}, - }, - - { - where: loc(), - val: int(42), - str: "42", - in: []string{"test", "-s", "42"}, - }, - { - where: loc(), - val: int8(42), - str: "42", - in: []string{"test", "-s", "42"}, - }, - { - where: loc(), - val: int16(42), - str: "42", - in: []string{"test", "-s", "42"}, - }, - { - where: loc(), - val: int32(42), - str: "42", - in: []string{"test", "-s", "42"}, - }, - { - where: loc(), - val: int64(42), - str: "42", - in: []string{"test", "-s", "42"}, - }, - - { - where: loc(), - val: uint(42), - str: "42", - in: []string{"test", "-s", "42"}, - }, - { - where: loc(), - val: uint8(42), - str: "42", - in: []string{"test", "-s", "42"}, - }, - { - where: loc(), - val: uint16(42), - str: "42", - in: []string{"test", "-s", "42"}, - }, - { - where: loc(), - val: uint32(42), - str: "42", - in: []string{"test", "-s", "42"}, - }, - { - where: loc(), - val: uint64(42), - str: "42", - in: []string{"test", "-s", "42"}, - }, - - { - where: loc(), - val: float32(4.2), - str: "4.2", - in: []string{"test", "-s", "4.2"}, - }, - { - where: loc(), - val: float64(4.2), - str: "4.2", - in: []string{"test", "-s", "4.2"}, - }, - - { - where: loc(), - val: time.Duration(time.Second * 42), - def: time.Second * 2, - str: "42s", - in: []string{"test", "-s", "42s"}, - }, - { - where: loc(), - val: time.Duration(time.Second * 42), - def: time.Second * 2, - str: "42s", - in: []string{"test", "-s42s"}, - }, - { - where: loc(), - val: time.Duration(time.Second * 2), - def: time.Second * 2, - in: []string{"test", "-s42"}, - str: "2s", - err: "test: time: missing unit in duration 42", - }, - - { - where: loc(), - val: []string{"42", "."}, - str: "42,.", - def: []string{"one", "two", "three"}, - in: []string{"test", "-s42", "-s."}, - }, - { - where: loc(), - val: []string{"42", "."}, - str: "42,.", - def: []string{"one", "two", "three"}, - in: []string{"test", "-s42,."}, - }, - { - where: loc(), - val: []string{"one", "two", "three"}, - def: []string{"one", "two", "three"}, - str: "one,two,three", - in: []string{"test"}, - }, - } { - reset() - var opt Option - val := reflect.New(reflect.TypeOf(tt.val)).Interface() - if tt.def != nil { - reflect.ValueOf(val).Elem().Set(reflect.ValueOf(tt.def)) - } - switch tt.kind { - case shortTest: - opt = Flag(val, 's') - case longTest: - opt = FlagLong(val, "long", 0) - case bothTest: - opt = FlagLong(val, "both", 'b') - } - _ = opt - parse(tt.in) - if s := checkError(tt.err); s != "" { - t.Errorf("%s: %s", tt.where, s) - continue - } - got := reflect.ValueOf(val).Elem().Interface() - want := reflect.ValueOf(tt.val).Interface() - if !reflect.DeepEqual(got, want) { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - if str := opt.String(); str != tt.str { - t.Errorf("%s: got string %q, want %q", tt.where, str, tt.str) - } - } -} - -func TestGenericDup(t *testing.T) { - defer func() { - stderr = os.Stderr - exit = os.Exit - }() - - reset() - var v1, v2 string - type myPanic struct{} - var errbuf bytes.Buffer - stderr = &errbuf - _, file, line, _ := runtime.Caller(0) - Flag(&v1, 's') - line++ // line is now the line number of the first call to Flag. - - exit = func(i int) { panic(myPanic{}) } - defer func() { - p := recover() - if _, ok := p.(myPanic); ok { - err := errbuf.String() - if !strings.Contains(err, "-s already declared") || !strings.Contains(err, fmt.Sprintf("%s:%d", file, line)) { - t.Errorf("unexpected error: %q\nshould contain \"-s already declared\" and \"%s:%d\"", err, file, line) - } - } else if p == nil { - t.Errorf("Second call to Flag did not fail") - } else { - t.Errorf("panic %v", p) - } - }() - Flag(&v2, 's') -} - -func TestGenericDupNested(t *testing.T) { - defer func() { - stderr = os.Stderr - exit = os.Exit - }() - - reset() - type myPanic struct{} - var errbuf bytes.Buffer - stderr = &errbuf - _, file, line, _ := runtime.Caller(0) - String('s', "default") - line++ // line is now the line number of the first call to Flag. - - exit = func(i int) { panic(myPanic{}) } - defer func() { - p := recover() - if _, ok := p.(myPanic); ok { - err := errbuf.String() - if !strings.Contains(err, "-s already declared") || !strings.Contains(err, fmt.Sprintf("%s:%d", file, line)) { - t.Errorf("unexpected error: %q\nshould contain \"-s already declared\" and \"%s:%d\"", err, file, line) - } - } else if p == nil { - t.Errorf("Second call to Flag did not fail") - } else { - t.Errorf("panic %v", p) - } - }() - String('s', "default") -} diff --git a/v2/getopt.go b/v2/getopt.go deleted file mode 100644 index 1ffbfaa..0000000 --- a/v2/getopt.go +++ /dev/null @@ -1,564 +0,0 @@ -// Copyright 2017 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package getopt (v2) provides traditional getopt processing for implementing -// commands that use traditional command lines. The standard Go flag package -// cannot be used to write a program that parses flags the way ls or ssh does, -// for example. Version 2 of this package has a simplified API. -// -// See the github.com/pborman/options package for a simple structure based -// interface to this package. -// -// USAGE -// -// Getopt supports functionality found in both the standard BSD getopt as well -// as (one of the many versions of) the GNU getopt_long. Being a Go package, -// this package makes common usage easy, but still enables more controlled usage -// if needed. -// -// Typical usage: -// -// // Declare flags and have getopt return pointers to the values. -// helpFlag := getopt.Bool('?', "display help") -// cmdFlag := getopt.StringLong("command", 'c', "default", "the command") -// -// // Declare flags against existing variables. -// var { -// fileName = "/the/default/path" -// timeout = time.Second * 5 -// verbose bool -// } -// func init() { -// getopt.Flag(&verbose, 'v', "be verbose") -// getopt.FlagLong(&fileName, "path", 0, "the path") -// getopt.FlagLong(&timeout, "timeout", 't', "some timeout") -// } -// -// func main() { -// // Parse the program arguments -// getopt.Parse() -// // Get the remaining positional parameters -// args := getopt.Args() -// ... -// -// If you don't want the program to exit on error, use getopt.Getopt: -// -// err := getopt.Getopt(nil) -// if err != nil { -// // code to handle error -// fmt.Fprintln(os.Stderr, err) -// } -// -// FLAG SYNTAX -// -// Support is provided for both short (-f) and long (--flag) options. A single -// option may have both a short and a long name. Each option may be a flag or a -// value. A value takes an argument. -// -// Declaring no long names causes this package to process arguments like the -// traditional BSD getopt. -// -// Short flags may be combined into a single parameter. For example, "-a -b -c" -// may also be expressed "-abc". Long flags must stand on their own "--alpha -// --beta" -// -// Values require an argument. For short options the argument may either be -// immediately following the short name or as the next argument. Only one short -// value may be combined with short flags in a single argument; the short value -// must be after all short flags. For example, if f is a flag and v is a value, -// then: -// -// -vvalue (sets v to "value") -// -v value (sets v to "value") -// -fvvalue (sets f, and sets v to "value") -// -fv value (sets f, and sets v to "value") -// -vf value (set v to "f" and value is the first parameter) -// -// For the long value option val: -// -// --val value (sets val to "value") -// --val=value (sets val to "value") -// --valvalue (invalid option "valvalue") -// -// Values with an optional value only set the value if the value is part of the -// same argument. In any event, the option count is increased and the option is -// marked as seen. -// -// -v -f (sets v and f as being seen) -// -vvalue -f (sets v to "value" and sets f) -// --val -f (sets v and f as being seen) -// --val=value -f (sets v to "value" and sets f) -// -// There is no convenience function defined for making the value optional. The -// SetOptional method must be called on the actual Option. -// -// v := String("val", 'v', "", "the optional v") -// Lookup("v").SetOptional() -// -// var s string -// FlagLong(&s, "val", 'v', "the optional v).SetOptional() -// -// Parsing continues until the first non-option or "--" is encountered. -// -// The short name "-" can be used, but it either is specified as "-" or as part -// of a group of options, for example "-f-". If there are no long options -// specified then "--f" could also be used. If "-" is not declared as an option -// then the single "-" will also terminate the option processing but unlike -// "--", the "-" will be part of the remaining arguments. -// -// ADVANCED USAGE -// -// Normally the parsing is performed by calling the Parse function. If it is -// important to see the order of the options then the Getopt function should be -// used. The standard Parse function does the equivalent of: -// -// func Parse() { -// if err := getopt.Getopt(os.Args, nil); err != nil { -// fmt.Fprintln(os.Stderr, err) -// s.usage() -// os.Exit(1) -// } -// -// When calling Getopt it is the responsibility of the caller to print any -// errors. -// -// Normally the default option set, CommandLine, is used. Other option sets may -// be created with New. -// -// After parsing, the sets Args will contain the non-option arguments. If an -// error is encountered then Args will begin with argument that caused the -// error. -// -// It is valid to call a set's Parse a second time to amen flags or values. As -// an example: -// -// var a = getopt.Bool('a', "", "The a flag") -// var b = getopt.Bool('b', "", "The a flag") -// var cmd = "" -// -// var opts = getopt.CommandLine -// -// opts.Parse(os.Args) -// if opts.NArgs() > 0 { -// cmd = opts.Arg(0) -// opts.Parse(opts.Args()) -// } -// -// If called with set to { "prog", "-a", "cmd", "-b", "arg" } then both and and -// b would be set, cmd would be set to "cmd", and opts.Args() would return { -// "arg" }. -// -// Unless an option type explicitly prohibits it, an option may appear more than -// once in the arguments. The last value provided to the option is the value. -// -// BUILTIN TYPES -// -// The Flag and FlagLong functions support most standard Go types. For the -// list, see the description of FlagLong below for a list of supported types. -// -// There are also helper routines to allow single line flag declarations. These -// types are: Bool, Counter, Duration, Enum, Int16, Int32, Int64, Int, List, -// Signed, String, Uint16, Uint32, Uint64, Uint, and Unsigned. -// -// Each comes in a short and long flavor, e.g., Bool and BoolLong and include -// functions to set the flags on the standard command line or for a specific Set -// of flags. -// -// Except for the Counter, Enum, Signed and Unsigned types, all of these types -// can be declared using Flag and FlagLong by passing in a pointer to the -// appropriate type. -// -// DECLARING NEW FLAG TYPES -// -// A pointer to any type that implements the Value interface may be passed to -// Flag or FlagLong. -// -// VALUEHELP -// -// All non-flag options are created with a "valuehelp" as the last parameter. -// Valuehelp should be 0, 1, or 2 strings. The first string, if provided, is -// the usage message for the option. If the second string, if provided, is the -// name to use for the value when displaying the usage. If not provided the -// term "value" is assumed. -// -// The usage message for the option created with -// -// StringLong("option", 'o', "defval", "a string of letters") -// -// is -// -// -o, -option=value -// -// StringLong("option", 'o', "defval", "a string of letters", "string") -// -// is -// -// -o, -option=string -package getopt - -import ( - "fmt" - "io" - "os" - "path" - "sort" - "strings" - "time" -) - -// stderr allows tests to capture output to standard error. -var stderr io.Writer = os.Stderr - -// exit allows tests to capture an os.Exit call -var exit = os.Exit - -// DisplayWidth is used to determine where to split usage long lines. -var DisplayWidth = 80 - -// HelpColumn is the maximum column position that help strings start to display -// at. If the option usage is too long then the help string will be displayed -// on the next line. For example: -// -// -a this is the a flag -// -u, --under=location -// the u flag's usage is quite long -var HelpColumn = 20 - -// PrintUsage prints the usage line and set of options of set S to w. -func (s *Set) PrintUsage(w io.Writer) { - parts := make([]string, 2, 4) - parts[0] = "Usage:" - parts[1] = s.program - if usage := s.UsageLine(); usage != "" { - parts = append(parts, usage) - } - if s.parameters != "" { - parts = append(parts, s.parameters) - } - fmt.Fprintln(w, strings.Join(parts, " ")) - s.PrintOptions(w) -} - -// UsageLine returns the usage line for the set s. The set's program name and -// parameters, if any, are not included. -func (s *Set) UsageLine() string { - sort.Sort(s.options) - flags := "" - - // Build up the list of short flag names and also compute - // how to display the option in the longer help listing. - // We also keep track of the longest option usage string - // that is no more than HelpColumn-3 bytes (at which point - // we use two lines to display the help). The three - // is for the leading space and the two spaces before the - // help string. - for _, opt := range s.options { - if opt.name == "" { - opt.name = "value" - } - if opt.uname == "" { - opt.uname = opt.usageName() - } - if opt.flag && opt.short != 0 && opt.short != '-' { - flags += string(opt.short) - } - } - - var opts []string - - // The short option - is special - if s.shortOptions['-'] != nil { - opts = append(opts, "-") - } - - // If we have a bundle of flags, add them to the list - if flags != "" { - opts = append(opts, "-"+flags) - } - - // Now append all the long options and options that require - // values. - for _, opt := range s.options { - if opt.flag { - if opt.short != 0 { - continue - } - flags = "--" + opt.long - } else if opt.short != 0 { - flags = "-" + string(opt.short) + " " + opt.name - } else { - flags = "--" + string(opt.long) + " " + opt.name - } - opts = append(opts, flags) - } - flags = strings.Join(opts, "] [") - if flags != "" { - flags = "[" + flags + "]" - } - return flags -} - -// PrintOptions prints the list of options in s to w. -func (s *Set) PrintOptions(w io.Writer) { - sort.Sort(s.options) - max := 4 - for _, opt := range s.options { - if opt.name == "" { - opt.name = "value" - } - if opt.uname == "" { - opt.uname = opt.usageName() - } - if max < len(opt.uname) && len(opt.uname) <= HelpColumn-3 { - max = len(opt.uname) - } - } - // Now print one or more usage lines per option. - for _, opt := range s.options { - if opt.uname != "" { - opt.help = strings.TrimSpace(opt.help) - if len(opt.help) == 0 { - fmt.Fprintf(w, " %s\n", opt.uname) - continue - } - helpMsg := opt.help - - // If the default value is the known zero value - // then don't display it. - def := opt.defval - switch genericValue(opt.value).(type) { - case *bool: - if def == "false" { - def = "" - } - case *int, *int8, *int16, *int32, *int64, - *uint, *uint8, *uint16, *uint32, *uint64, - *float32, *float64: - if def == "0" { - def = "" - } - case *time.Duration: - if def == "0s" { - def = "" - } - default: - if opt.flag && def == "false" { - def = "" - } - } - if def != "" { - helpMsg += " [" + def + "]" - } - - help := strings.Split(helpMsg, "\n") - // If they did not put in newlines then we will insert - // them to keep the help messages from wrapping. - if len(help) == 1 { - help = breakup(help[0], DisplayWidth-HelpColumn) - } - if len(opt.uname) <= max { - fmt.Fprintf(w, " %-*s %s\n", max, opt.uname, help[0]) - help = help[1:] - } else { - fmt.Fprintf(w, " %s\n", opt.uname) - } - for _, s := range help { - fmt.Fprintf(w, " %-*s %s\n", max, " ", s) - } - } - } -} - -// breakup breaks s up into strings no longer than max bytes. -func breakup(s string, max int) []string { - var a []string - - for { - // strip leading spaces - for len(s) > 0 && s[0] == ' ' { - s = s[1:] - } - // If the option is no longer than the max just return it - if len(s) <= max { - if len(s) != 0 { - a = append(a, s) - } - return a - } - x := max - for s[x] != ' ' { - // the first word is too long?! - if x == 0 { - x = max - for x < len(s) && s[x] != ' ' { - x++ - } - if x == len(s) { - x-- - } - break - } - x-- - } - for s[x] == ' ' { - x-- - } - a = append(a, s[:x+1]) - s = s[x+1:] - } -} - -// Parse uses Getopt to parse args using the options set for s. The first -// element of args is used to assign the program for s if it is not yet set. On -// error, Parse displays the error message as well as a usage message on -// standard error and then exits the program. -func (s *Set) Parse(args []string) { - if err := s.Getopt(args, nil); err != nil { - fmt.Fprintln(stderr, err) - s.usage() - exit(1) - } -} - -// Parse uses Getopt to parse args using the options set for s. The first -// element of args is used to assign the program for s if it is not yet set. -// Getop calls fn, if not nil, for each option parsed. -// -// Getopt returns nil when all options have been processed (a non-option -// argument was encountered, "--" was encountered, or fn returned false). -// -// On error getopt returns a reference to an InvalidOption (which implements the -// error interface). -func (s *Set) Getopt(args []string, fn func(Option) bool) (err error) { - s.setState(InProgress) - defer func() { - if s.State() == InProgress { - switch { - case err != nil: - s.setState(Failure) - case len(s.args) == 0: - s.setState(EndOfArguments) - default: - s.setState(Unknown) - } - } - }() - if fn == nil { - fn = func(Option) bool { return true } - } - if len(args) == 0 { - return nil - } - - if s.program == "" { - s.program = path.Base(args[0]) - } - args = args[1:] -Parsing: - for len(args) > 0 { - arg := args[0] - s.args = args - args = args[1:] - - // end of options? - if arg == "" || arg[0] != '-' { - s.setState(EndOfOptions) - return nil - } - - if arg == "-" { - goto ShortParsing - } - - // explicitly request end of options? - if arg == "--" { - s.args = args - s.setState(DashDash) - return nil - } - - // Long option processing - if len(s.longOptions) > 0 && arg[1] == '-' { - e := strings.IndexRune(arg, '=') - var value string - if e > 0 { - value = arg[e+1:] - arg = arg[:e] - } - opt := s.longOptions[arg[2:]] - // If we are processing long options then --f is -f - // if f is not defined as a long option. - // This lets you say --f=false - if opt == nil && len(arg[2:]) == 1 { - opt = s.shortOptions[rune(arg[2])] - } - if opt == nil { - return unknownOption(arg[2:]) - } - opt.isLong = true - // If we require an option and did not have an = - // then use the next argument as an option. - if !opt.flag && e < 0 && !opt.optional { - if len(args) == 0 { - return missingArg(opt) - } - value = args[0] - args = args[1:] - } - opt.count++ - - if err := opt.value.Set(value, opt); err != nil { - return setError(opt, value, err) - } - - if !fn(opt) { - s.setState(Terminated) - return nil - } - continue Parsing - } - - // Short option processing - arg = arg[1:] // strip - - ShortParsing: - for i, c := range arg { - opt := s.shortOptions[c] - if opt == nil { - // In traditional getopt, if - is not registered - // as an option, a lone - is treated as - // if there were a -- in front of it. - if arg == "-" { - s.setState(Dash) - return nil - } - return unknownOption(c) - } - opt.isLong = false - opt.count++ - var value string - if !opt.flag { - value = arg[1+i:] - if value == "" && !opt.optional { - if len(args) == 0 { - return missingArg(opt) - } - value = args[0] - args = args[1:] - } - } - if err := opt.value.Set(value, opt); err != nil { - return setError(opt, value, err) - } - if !fn(opt) { - s.setState(Terminated) - return nil - } - if !opt.flag { - continue Parsing - } - } - } - s.args = []string{} - return nil -} diff --git a/v2/help_test.go b/v2/help_test.go deleted file mode 100644 index c7b5bb0..0000000 --- a/v2/help_test.go +++ /dev/null @@ -1,145 +0,0 @@ -package getopt - -import ( - "bytes" - "fmt" - "strings" - "testing" - "time" -) - -type flagValue bool - -func (f *flagValue) Set(value string, opt Option) error { - switch strings.ToLower(value) { - case "true", "t", "on", "1": - *f = true - case "false", "f", "off", "0": - *f = false - default: - return fmt.Errorf("invalid flagValue %q", value) - } - return nil -} -func (f *flagValue) String() string { - return fmt.Sprint(bool(*f)) -} - -func TestHelpDefaults(t *testing.T) { - HelpColumn = 40 - set := New() - bf := false - bt := true - set.FlagLong(&bf, "bool_false", 'f', "false bool value") - set.FlagLong(&bt, "bool_true", 't', "true bool value") - i := int(0) - i8 := int8(0) - i16 := int16(0) - i32 := int32(0) - i64 := int64(0) - si := int(1) - si8 := int8(8) - si16 := int16(16) - si32 := int32(32) - si64 := int64(64) - ui := uint(0) - ui8 := uint8(0) - ui16 := uint16(0) - ui32 := uint32(0) - ui64 := uint64(0) - sui := uint(1) - sui8 := uint8(8) - sui16 := uint16(16) - sui32 := uint32(32) - sui64 := uint64(64) - - set.FlagLong(&i, "int", 0, "int value") - set.FlagLong(&si, "int_set", 0, "set int value") - set.FlagLong(&i8, "int8", 0, "int8 value") - set.FlagLong(&si8, "int8_set", 0, "set int8 value") - set.FlagLong(&i16, "int16", 0, "int16 value") - set.FlagLong(&si16, "int16_set", 0, "set int16 value") - set.FlagLong(&i32, "int32", 0, "int32 value") - set.FlagLong(&si32, "int32_set", 0, "set int32 value") - set.FlagLong(&i64, "int64", 0, "int64 value") - set.FlagLong(&si64, "int64_set", 0, "set int64 value") - - set.FlagLong(&ui, "uint", 0, "uint value") - set.FlagLong(&sui, "uint_set", 0, "set uint value") - set.FlagLong(&ui8, "uint8", 0, "uint8 value") - set.FlagLong(&sui8, "uint8_set", 0, "set uint8 value") - set.FlagLong(&ui16, "uint16", 0, "uint16 value") - set.FlagLong(&sui16, "uint16_set", 0, "set uint16 value") - set.FlagLong(&ui32, "uint32", 0, "uint32 value") - set.FlagLong(&sui32, "uint32_set", 0, "set uint32 value") - set.FlagLong(&ui64, "uint64", 0, "uint64 value") - set.FlagLong(&sui64, "uint64_set", 0, "set uint64 value") - - f32 := float32(0) - f64 := float64(0) - sf32 := float32(3.2) - sf64 := float64(6.4) - - set.FlagLong(&f32, "float32", 0, "float32 value") - set.FlagLong(&sf32, "float32_set", 0, "set float32 value") - set.FlagLong(&f64, "float64", 0, "float64 value") - set.FlagLong(&sf64, "float64_set", 0, "set float64 value") - - d := time.Duration(0) - sd := time.Duration(time.Second) - - set.FlagLong(&d, "duration", 0, "duration value") - set.FlagLong(&sd, "duration_set", 0, "set duration value") - - str := "" - sstr := "string" - set.FlagLong(&str, "string", 0, "string value") - set.FlagLong(&sstr, "string_set", 0, "set string value") - - var fv flagValue - set.FlagLong(&fv, "vbool", 0, "value bool").SetFlag() - - var fvo flagValue = true - set.FlagLong(&fvo, "vbool_on", 0, "value bool").SetFlag() - - want := ` - --duration=value duration value - --duration_set=value set duration value [1s] - -f, --bool_false false bool value - --float32=value float32 value - --float32_set=value set float32 value [3.2] - --float64=value float64 value - --float64_set=value set float64 value [6.4] - --int=value int value - --int16=value int16 value - --int16_set=value set int16 value [16] - --int32=value int32 value - --int32_set=value set int32 value [32] - --int64=value int64 value - --int64_set=value set int64 value [64] - --int8=value int8 value - --int8_set=value set int8 value [8] - --int_set=value set int value [1] - --string=value string value - --string_set=value set string value [string] - -t, --bool_true true bool value [true] - --uint=value uint value - --uint16=value uint16 value - --uint16_set=value set uint16 value [16] - --uint32=value uint32 value - --uint32_set=value set uint32 value [32] - --uint64=value uint64 value - --uint64_set=value set uint64 value [64] - --uint8=value uint8 value - --uint8_set=value set uint8 value [8] - --uint_set=value set uint value [1] - --vbool value bool - --vbool_on value bool [true] -`[1:] - - var buf bytes.Buffer - set.PrintOptions(&buf) - if got := buf.String(); got != want { - t.Errorf("got:\n%s\nwant:\n%s", got, want) - } -} diff --git a/v2/int.go b/v2/int.go deleted file mode 100644 index 42833c1..0000000 --- a/v2/int.go +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2017 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -// Int creates an option that parses its value as an integer. -func Int(name rune, value int, helpvalue ...string) *int { - return CommandLine.Int(name, value, helpvalue...) -} - -func (s *Set) Int(name rune, value int, helpvalue ...string) *int { - s.Flag(&value, name, helpvalue...) - return &value -} - -func IntLong(name string, short rune, value int, helpvalue ...string) *int { - return CommandLine.IntLong(name, short, value, helpvalue...) -} - -func (s *Set) IntLong(name string, short rune, value int, helpvalue ...string) *int { - s.FlagLong(&value, name, short, helpvalue...) - return &value -} - -// Int16 creates an option that parses its value as a 16 bit integer. -func Int16(name rune, value int16, helpvalue ...string) *int16 { - return CommandLine.Int16(name, value, helpvalue...) -} - -func (s *Set) Int16(name rune, value int16, helpvalue ...string) *int16 { - s.Flag(&value, name, helpvalue...) - return &value -} - -func Int16Long(name string, short rune, value int16, helpvalue ...string) *int16 { - return CommandLine.Int16Long(name, short, value, helpvalue...) -} - -func (s *Set) Int16Long(name string, short rune, value int16, helpvalue ...string) *int16 { - s.FlagLong(&value, name, short, helpvalue...) - return &value -} - -// Int32 creates an option that parses its value as a 32 bit integer. -func Int32(name rune, value int32, helpvalue ...string) *int32 { - return CommandLine.Int32(name, value, helpvalue...) -} - -func (s *Set) Int32(name rune, value int32, helpvalue ...string) *int32 { - s.Flag(&value, name, helpvalue...) - return &value -} - -func Int32Long(name string, short rune, value int32, helpvalue ...string) *int32 { - return CommandLine.Int32Long(name, short, value, helpvalue...) -} - -func (s *Set) Int32Long(name string, short rune, value int32, helpvalue ...string) *int32 { - s.FlagLong(&value, name, short, helpvalue...) - return &value -} - -// Int64 creates an option that parses its value as a 64 bit integer. -func Int64(name rune, value int64, helpvalue ...string) *int64 { - return CommandLine.Int64(name, value, helpvalue...) -} - -func (s *Set) Int64(name rune, value int64, helpvalue ...string) *int64 { - s.Flag(&value, name, helpvalue...) - return &value -} - -func Int64Long(name string, short rune, value int64, helpvalue ...string) *int64 { - return CommandLine.Int64Long(name, short, value, helpvalue...) -} - -func (s *Set) Int64Long(name string, short rune, value int64, helpvalue ...string) *int64 { - s.FlagLong(&value, name, short, helpvalue...) - return &value -} - -// Uint creates an option that parses its value as an unsigned integer. -func Uint(name rune, value uint, helpvalue ...string) *uint { - return CommandLine.Uint(name, value, helpvalue...) -} - -func (s *Set) Uint(name rune, value uint, helpvalue ...string) *uint { - s.Flag(&value, name, helpvalue...) - return &value -} - -func UintLong(name string, short rune, value uint, helpvalue ...string) *uint { - return CommandLine.UintLong(name, short, value, helpvalue...) -} - -func (s *Set) UintLong(name string, short rune, value uint, helpvalue ...string) *uint { - s.FlagLong(&value, name, short, helpvalue...) - return &value -} - -// Uint16 creates an option that parses its value as a 16 bit unsigned integer. -func Uint16(name rune, value uint16, helpvalue ...string) *uint16 { - return CommandLine.Uint16(name, value, helpvalue...) -} - -func (s *Set) Uint16(name rune, value uint16, helpvalue ...string) *uint16 { - s.Flag(&value, name, helpvalue...) - return &value -} - -func Uint16Long(name string, short rune, value uint16, helpvalue ...string) *uint16 { - return CommandLine.Uint16Long(name, short, value, helpvalue...) -} - -func (s *Set) Uint16Long(name string, short rune, value uint16, helpvalue ...string) *uint16 { - s.FlagLong(&value, name, short, helpvalue...) - return &value -} - -// Uint32 creates an option that parses its value as a 32 bit unsigned integer. -func Uint32(name rune, value uint32, helpvalue ...string) *uint32 { - return CommandLine.Uint32(name, value, helpvalue...) -} - -func (s *Set) Uint32(name rune, value uint32, helpvalue ...string) *uint32 { - s.Flag(&value, name, helpvalue...) - return &value -} - -func Uint32Long(name string, short rune, value uint32, helpvalue ...string) *uint32 { - return CommandLine.Uint32Long(name, short, value, helpvalue...) -} - -func (s *Set) Uint32Long(name string, short rune, value uint32, helpvalue ...string) *uint32 { - s.FlagLong(&value, name, short, helpvalue...) - return &value -} - -// Uint64 creates an option that parses its value as a 64 bit unsigned integer. -func Uint64(name rune, value uint64, helpvalue ...string) *uint64 { - return CommandLine.Uint64(name, value, helpvalue...) -} - -func (s *Set) Uint64(name rune, value uint64, helpvalue ...string) *uint64 { - s.Flag(&value, name, helpvalue...) - return &value -} - -func Uint64Long(name string, short rune, value uint64, helpvalue ...string) *uint64 { - return CommandLine.Uint64Long(name, short, value, helpvalue...) -} - -func (s *Set) Uint64Long(name string, short rune, value uint64, helpvalue ...string) *uint64 { - s.FlagLong(&value, name, short, helpvalue...) - return &value -} diff --git a/v2/int_test.go b/v2/int_test.go deleted file mode 100644 index 57175fd..0000000 --- a/v2/int_test.go +++ /dev/null @@ -1,595 +0,0 @@ -// Copyright 2017 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -import ( - "fmt" - "strings" - "testing" -) - -var intTests = []struct { - where string - in []string - i int - int int - err string -}{ - { - loc(), - []string{}, - 17, 42, - "", - }, - { - loc(), - []string{"test", "-i", "1", "--int", "2"}, - 1, 2, - "", - }, - { - loc(), - []string{"test", "-i1", "--int=2"}, - 1, 2, - "", - }, - { - loc(), - []string{"test", "-i1", "-i2"}, - 2, 42, - "", - }, - { - loc(), - []string{"test", "-i=1"}, - 17, 42, - "test: not a valid number: =1\n", - }, - { - loc(), - []string{"test", "-i0x20"}, - 0x20, 42, - "", - }, - { - loc(), - []string{"test", "-i010"}, - 8, 42, - "", - }, -} - -func TestInt(t *testing.T) { - for x, tt := range intTests { - reset() - i := Int('i', 17) - opt := IntLong("int", 0, 42) - if strings.Index(tt.where, ":-") > 0 { - tt.where = fmt.Sprintf("#%d", x) - } - - parse(tt.in) - if s := checkError(tt.err); s != "" { - t.Errorf("%s: %s", tt.where, s) - } - if got, want := *i, tt.i; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - if got, want := *opt, tt.int; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - } -} - -var int16Tests = []struct { - where string - in []string - i int16 - int16 int16 - err string -}{ - { - loc(), - []string{}, - 17, 42, - "", - }, - { - loc(), - []string{"test", "-i", "1", "--int16", "2"}, - 1, 2, - "", - }, - { - loc(), - []string{"test", "-i1", "--int16=2"}, - 1, 2, - "", - }, - { - loc(), - []string{"test", "-i1", "-i2"}, - 2, 42, - "", - }, - { - loc(), - []string{"test", "-i=1"}, - 17, 42, - "test: not a valid number: =1\n", - }, - { - loc(), - []string{"test", "-i0x20"}, - 0x20, 42, - "", - }, - { - loc(), - []string{"test", "-i010"}, - 8, 42, - "", - }, -} - -func TestInt16(t *testing.T) { - for x, tt := range int16Tests { - reset() - i := Int16('i', 17) - opt := Int16Long("int16", 0, 42) - if strings.Index(tt.where, ":-") > 0 { - tt.where = fmt.Sprintf("#%d", x) - } - - parse(tt.in) - if s := checkError(tt.err); s != "" { - t.Errorf("%s: %s", tt.where, s) - } - if got, want := *i, tt.i; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - if got, want := *opt, tt.int16; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - } -} - -var int32Tests = []struct { - where string - in []string - i int32 - int32 int32 - err string -}{ - { - loc(), - []string{}, - 17, 42, - "", - }, - { - loc(), - []string{"test", "-i", "1", "--int32", "2"}, - 1, 2, - "", - }, - { - loc(), - []string{"test", "-i1", "--int32=2"}, - 1, 2, - "", - }, - { - loc(), - []string{"test", "-i1", "-i2"}, - 2, 42, - "", - }, - { - loc(), - []string{"test", "-i=1"}, - 17, 42, - "test: not a valid number: =1\n", - }, - { - loc(), - []string{"test", "-i0x20"}, - 0x20, 42, - "", - }, - { - loc(), - []string{"test", "-i010"}, - 8, 42, - "", - }, -} - -func TestInt32(t *testing.T) { - for x, tt := range int32Tests { - reset() - i := Int32('i', 17) - opt := Int32Long("int32", 0, 42) - if strings.Index(tt.where, ":-") > 0 { - tt.where = fmt.Sprintf("#%d", x) - } - - parse(tt.in) - if s := checkError(tt.err); s != "" { - t.Errorf("%s: %s", tt.where, s) - } - if got, want := *i, tt.i; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - if got, want := *opt, tt.int32; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - } -} - -var int64Tests = []struct { - where string - in []string - i int64 - int64 int64 - err string -}{ - { - loc(), - []string{}, - 17, 42, - "", - }, - { - loc(), - []string{"test", "-i", "1", "--int64", "2"}, - 1, 2, - "", - }, - { - loc(), - []string{"test", "-i1", "--int64=2"}, - 1, 2, - "", - }, - { - loc(), - []string{"test", "-i1", "-i2"}, - 2, 42, - "", - }, - { - loc(), - []string{"test", "-i=1"}, - 17, 42, - "test: not a valid number: =1\n", - }, - { - loc(), - []string{"test", "-i0x20"}, - 0x20, 42, - "", - }, - { - loc(), - []string{"test", "-i010"}, - 8, 42, - "", - }, -} - -func TestInt64(t *testing.T) { - for x, tt := range int64Tests { - reset() - i := Int64('i', 17) - opt := Int64Long("int64", 0, 42) - if strings.Index(tt.where, ":-") > 0 { - tt.where = fmt.Sprintf("#%d", x) - } - - parse(tt.in) - if s := checkError(tt.err); s != "" { - t.Errorf("%s: %s", tt.where, s) - } - if got, want := *i, tt.i; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - if got, want := *opt, tt.int64; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - } -} - -var uintTests = []struct { - where string - in []string - i uint - uint uint - err string -}{ - { - loc(), - []string{}, - 17, 42, - "", - }, - { - loc(), - []string{"test", "-i", "1", "--uint", "2"}, - 1, 2, - "", - }, - { - loc(), - []string{"test", "-i1", "--uint=2"}, - 1, 2, - "", - }, - { - loc(), - []string{"test", "-i1", "-i2"}, - 2, 42, - "", - }, - { - loc(), - []string{"test", "-i=1"}, - 17, 42, - "test: not a valid number: =1\n", - }, - { - loc(), - []string{"test", "-i0x20"}, - 0x20, 42, - "", - }, - { - loc(), - []string{"test", "-i010"}, - 8, 42, - "", - }, -} - -func TestUint(t *testing.T) { - for x, tt := range uintTests { - reset() - i := Uint('i', 17) - opt := UintLong("uint", 0, 42) - if strings.Index(tt.where, ":-") > 0 { - tt.where = fmt.Sprintf("#%d", x) - } - - parse(tt.in) - if s := checkError(tt.err); s != "" { - t.Errorf("%s: %s", tt.where, s) - } - if got, want := *i, tt.i; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - if got, want := *opt, tt.uint; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - } -} - -var uint16Tests = []struct { - where string - in []string - i uint16 - uint16 uint16 - err string -}{ - { - loc(), - []string{}, - 17, 42, - "", - }, - { - loc(), - []string{"test", "-i", "1", "--uint16", "2"}, - 1, 2, - "", - }, - { - loc(), - []string{"test", "-i1", "--uint16=2"}, - 1, 2, - "", - }, - { - loc(), - []string{"test", "-i1", "-i2"}, - 2, 42, - "", - }, - { - loc(), - []string{"test", "-i=1"}, - 17, 42, - "test: not a valid number: =1\n", - }, - { - loc(), - []string{"test", "-i0x20"}, - 0x20, 42, - "", - }, - { - loc(), - []string{"test", "-i010"}, - 8, 42, - "", - }, -} - -func TestUint16(t *testing.T) { - for x, tt := range uint16Tests { - reset() - i := Uint16('i', 17) - opt := Uint16Long("uint16", 0, 42) - if strings.Index(tt.where, ":-") > 0 { - tt.where = fmt.Sprintf("#%d", x) - } - - parse(tt.in) - if s := checkError(tt.err); s != "" { - t.Errorf("%s: %s", tt.where, s) - } - if got, want := *i, tt.i; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - if got, want := *opt, tt.uint16; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - } -} - -var uint32Tests = []struct { - where string - in []string - i uint32 - uint32 uint32 - err string -}{ - { - loc(), - []string{}, - 17, 42, - "", - }, - { - loc(), - []string{"test", "-i", "1", "--uint32", "2"}, - 1, 2, - "", - }, - { - loc(), - []string{"test", "-i1", "--uint32=2"}, - 1, 2, - "", - }, - { - loc(), - []string{"test", "-i1", "-i2"}, - 2, 42, - "", - }, - { - loc(), - []string{"test", "-i=1"}, - 17, 42, - "test: not a valid number: =1\n", - }, - { - loc(), - []string{"test", "-i0x20"}, - 0x20, 42, - "", - }, - { - loc(), - []string{"test", "-i010"}, - 8, 42, - "", - }, -} - -func TestUint32(t *testing.T) { - for x, tt := range uint32Tests { - reset() - i := Uint32('i', 17) - opt := Uint32Long("uint32", 0, 42) - if strings.Index(tt.where, ":-") > 0 { - tt.where = fmt.Sprintf("#%d", x) - } - - parse(tt.in) - if s := checkError(tt.err); s != "" { - t.Errorf("%s: %s", tt.where, s) - } - if got, want := *i, tt.i; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - if got, want := *opt, tt.uint32; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - } -} - -var uint64Tests = []struct { - where string - in []string - i uint64 - uint64 uint64 - err string -}{ - { - loc(), - []string{}, - 17, 42, - "", - }, - { - loc(), - []string{"test", "-i", "1", "--uint64", "2"}, - 1, 2, - "", - }, - { - loc(), - []string{"test", "-i1", "--uint64=2"}, - 1, 2, - "", - }, - { - loc(), - []string{"test", "-i1", "-i2"}, - 2, 42, - "", - }, - { - loc(), - []string{"test", "-i=1"}, - 17, 42, - "test: not a valid number: =1\n", - }, - { - loc(), - []string{"test", "-i0x20"}, - 0x20, 42, - "", - }, - { - loc(), - []string{"test", "-i010"}, - 8, 42, - "", - }, -} - -func TestUint64(t *testing.T) { - for x, tt := range uint64Tests { - reset() - i := Uint64('i', 17) - opt := Uint64Long("uint64", 0, 42) - if strings.Index(tt.where, ":-") > 0 { - tt.where = fmt.Sprintf("#%d", x) - } - - parse(tt.in) - if s := checkError(tt.err); s != "" { - t.Errorf("%s: %s", tt.where, s) - } - if got, want := *i, tt.i; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - if got, want := *opt, tt.uint64; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - } -} diff --git a/v2/list.go b/v2/list.go deleted file mode 100644 index 99a09ef..0000000 --- a/v2/list.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2017 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -// List creates an option that returns a slice of strings. The parameters -// passed are converted from a comma separated value list into a slice. -// Subsequent occurrences append to the list. -func List(name rune, helpvalue ...string) *[]string { - p := []string{} - CommandLine.Flag(&p, name, helpvalue...) - return &p -} - -func (s *Set) List(name rune, helpvalue ...string) *[]string { - p := []string{} - s.Flag(&p, name, helpvalue...) - return &p -} - -func ListLong(name string, short rune, helpvalue ...string) *[]string { - p := []string{} - CommandLine.FlagLong(&p, name, short, helpvalue...) - return &p -} - -func (s *Set) ListLong(name string, short rune, helpvalue ...string) *[]string { - p := []string{} - s.FlagLong(&p, name, short, helpvalue...) - return &p -} diff --git a/v2/list_test.go b/v2/list_test.go deleted file mode 100644 index e8c8d18..0000000 --- a/v2/list_test.go +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2017 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -import "testing" - -var listTests = []struct { - where string - in []string - l, list []string - err string -}{ - { - loc(), - []string{}, - nil, nil, - "", - }, - { - loc(), - []string{"test", "-l", "one"}, - []string{"one"}, nil, - "", - }, - { - loc(), - []string{"test", "-lone", "-ltwo"}, - []string{"one", "two"}, nil, - "", - }, - { - loc(), - []string{"test", "--list", "one"}, - nil, []string{"one"}, - "", - }, - { - loc(), - []string{"test", "--list=one", "--list=two"}, - nil, []string{"one", "two"}, - "", - }, - { - loc(), - []string{"test", "--list=one,two"}, - nil, []string{"one", "two"}, - "", - }, -} - -func TestList(t *testing.T) { - for _, tt := range listTests { - reset() - l := List('l') - list := ListLong("list", 0) - - parse(tt.in) - if s := checkError(tt.err); s != "" { - t.Errorf("%s: %s", tt.where, s) - } - if badSlice(*l, tt.l) { - t.Errorf("%s: got s = %q, want %q", tt.where, *l, tt.l) - } - if badSlice(*list, tt.list) { - t.Errorf("%s: got s = %q, want %q", tt.where, *list, tt.list) - } - } -} - -func TestDefaultList(t *testing.T) { - reset() - list := []string{"d1", "d2", "d3"} - Flag(&list, 'l') - parse([]string{"test"}) - - want := []string{"d1", "d2", "d3"} - if badSlice(list, want) { - t.Errorf("got s = %q, want %q", list, want) - } - - parse([]string{"test", "-l", "one"}) - want = []string{"one"} - if badSlice(list, want) { - t.Errorf("got s = %q, want %q", list, want) - } - - parse([]string{"test", "-l", "two"}) - want = []string{"one", "two"} - if badSlice(list, want) { - t.Errorf("got s = %q, want %q", list, want) - } - Lookup('l').Reset() - want = []string{"d1", "d2", "d3"} - if badSlice(list, want) { - t.Errorf("got s = %q, want %q", list, want) - } -} diff --git a/v2/option.go b/v2/option.go deleted file mode 100644 index 67822bc..0000000 --- a/v2/option.go +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright 2017 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -import ( - "fmt" - "strings" -) - -// An Option can be either a Flag or a Value -type Option interface { - // Name returns the name of the option. If the option has been seen - // then the last way it was referenced (short or long) is returned - // otherwise if there is a short name then this will be the short name - // as a string, else it will be the long name. - Name() string - - // ShortName always returns the short name of the option, or "" if there - // is no short name. The name does not include the "-". - ShortName() string - - // LongName always returns the long name of the option, or "" if there - // is no long name. The name does not include the "--". - LongName() string - - // IsFlag returns true if Option is a flag. - IsFlag() bool - - // Seen returns true if the flag was seen. - Seen() bool - - // Count returns the number of times the flag was seen. - Count() int - - // String returns the last value the option was set to. - String() string - - // Value returns the Value of the option. - Value() Value - - // SetOptional makes the value optional. The option and value are - // always a single argument. Either --option or --option=value. In - // the former case the value of the option does not change but the Set() - // will return true and the value returned by Count() is incremented. - // The short form is either -o or -ovalue. SetOptional returns - // the Option - SetOptional() Option - - // SetFlag makes the value a flag. Flags are boolean values and - // normally do not taken a value. They are set to true when seen. - // If a value is passed in the long form then it must be on, case - // insensitivinsensitive, one of "true", "false", "t", "f", "on", "off", "1", "0". - // SetFlag returns the Option - SetFlag() Option - - // Reset resets the state of the option so it appears it has not - // yet been seen, including resetting the value of the option - // to its original default state. - Reset() -} - -type option struct { - short rune // 0 means no short name - long string // "" means no long name - isLong bool // True if they used the long name - flag bool // true if a boolean flag - defval string // default value - optional bool // true if we take an optional value - help string // help message - where string // file where the option was defined - value Value // current value of option - count int // number of times we have seen this option - name string // name of the value (for usage) - uname string // name of the option (for usage) -} - -// usageName returns the name of the option for printing usage lines in one -// of the following forms: -// -// -f -// --flag -// -f, --flag -// -s value -// --set=value -// -s, --set=value -func (o *option) usageName() string { - // Don't print help messages if we have none and there is only one - // way to specify the option. - if o.help == "" && (o.short == 0 || o.long == "") { - return "" - } - n := "" - - switch { - case o.short != 0 && o.long == "": - n = "-" + string(o.short) - case o.short == 0 && o.long != "": - n = " --" + o.long - case o.short != 0 && o.long != "": - n = "-" + string(o.short) + ", --" + o.long - } - - switch { - case o.flag: - return n - case o.optional: - return n + "[=" + o.name + "]" - case o.long != "": - return n + "=" + o.name - } - return n + " " + o.name -} - -// sortName returns the name to sort the option on. -func (o *option) sortName() string { - if o.short != 0 { - return string(o.short) + o.long - } - return o.long[:1] + o.long -} - -func (o *option) Seen() bool { return o.count > 0 } -func (o *option) Count() int { return o.count } -func (o *option) IsFlag() bool { return o.flag } -func (o *option) String() string { return o.value.String() } -func (o *option) SetOptional() Option { o.optional = true; return o } -func (o *option) SetFlag() Option { o.flag = true; return o } - -func (o *option) Value() Value { - if o == nil { - return nil - } - return o.value -} - -func (o *option) Name() string { - if !o.isLong && o.short != 0 { - return "-" + string(o.short) - } - return "--" + o.long -} - -func (o *option) ShortName() string { - if o.short != 0 { - return string(o.short) - } - return "" -} - -func (o *option) LongName() string { - return o.long -} - -// Reset rests an option so that it appears it has not yet been seen. -func (o *option) Reset() { - o.isLong = false - o.count = 0 - o.value.Set(o.defval, o) -} - -type optionList []*option - -func (ol optionList) Len() int { return len(ol) } -func (ol optionList) Swap(i, j int) { ol[i], ol[j] = ol[j], ol[i] } -func (ol optionList) Less(i, j int) bool { - // first check the short names (or the first letter of the long name) - // If they are not equal (case insensitive) then we have our answer - n1 := ol[i].sortName() - n2 := ol[j].sortName() - l1 := strings.ToLower(n1) - l2 := strings.ToLower(n2) - if l1 < l2 { - return true - } - if l2 < l1 { - return false - } - return n1 < n2 -} - -// AddOption add the option o to set CommandLine if o is not already in set -// CommandLine. -func AddOption(o Option) { - CommandLine.AddOption(o) -} - -// AddOption add the option o to set s if o is not already in set s. -func (s *Set) AddOption(o Option) { - opt := o.(*option) - for _, eopt := range s.options { - if opt == eopt { - return - } - } - if opt.short != 0 { - if oo, ok := s.shortOptions[opt.short]; ok { - fmt.Fprintf(stderr, "%s: -%c already declared at %s\n", opt.where, opt.short, oo.where) - exit(1) - } - s.shortOptions[opt.short] = opt - } - if opt.long != "" { - if oo, ok := s.longOptions[opt.long]; ok { - fmt.Fprintf(stderr, "%s: --%s already declared at %s\n", opt.where, opt.long, oo.where) - exit(1) - } - s.longOptions[opt.long] = opt - } - s.options = append(s.options, opt) -} diff --git a/v2/set.go b/v2/set.go deleted file mode 100644 index a7895c8..0000000 --- a/v2/set.go +++ /dev/null @@ -1,293 +0,0 @@ -// Copyright 2017 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -import ( - "io" - "os" - "sort" - "sync" -) - -// A State is why the Getopt returned. -type State int - -const ( - InProgress = State(iota) // Getopt is still running - Dash // Returned on "-" - DashDash // Returned on "--" - EndOfOptions // End of options reached - EndOfArguments // No more arguments - Terminated // Terminated by callback function - Failure // Terminated due to error - Unknown // Indicates internal error -) - -type Set struct { - stateMu sync.Mutex - state State - - // args are the parameters remaining after parsing the optoins. - args []string - - // program is the name of the program for usage and error messages. - // If not set it will automatically be set to the base name of the - // first argument passed to parse. - program string - - // parameters is what is displayed on the usage line after displaying - // the various options. - parameters string - - usage func() // usage should print the programs usage and exit. - - shortOptions map[rune]*option - longOptions map[string]*option - options optionList -} - -// New returns a newly created option set. -func New() *Set { - s := &Set{ - shortOptions: make(map[rune]*option), - longOptions: make(map[string]*option), - parameters: "[parameters ...]", - } - - s.usage = func() { - s.PrintUsage(stderr) - } - return s -} - -func (s *Set) setState(state State) { - s.stateMu.Lock() - s.state = state - s.stateMu.Unlock() -} - -// State returns the current state of the Set s. The state is normally the -// reason the most recent call to Getopt returned. -func (s *Set) State() State { - s.stateMu.Lock() - defer s.stateMu.Unlock() - return s.state -} - -// The default set of command-line options. -var CommandLine = New() - -// PrintUsage calls PrintUsage in the default option set. -func PrintUsage(w io.Writer) { CommandLine.PrintUsage(w) } - -// Usage calls the usage function in the default option set. -func Usage() { CommandLine.usage() } - -// Parse calls Parse in the default option set with the command line arguments -// found in os.Args. -func Parse() { CommandLine.Parse(os.Args) } - -// Same as parse but not found in version 1 of getopt. -func ParseV2() { CommandLine.Parse(os.Args) } - -// Getops returns the result of calling Getop in the default option set with the -// command line arguments found in os.Args. The fn function, which may be nil, -// is passed to Getopt. -func Getopt(fn func(Option) bool) error { return CommandLine.Getopt(os.Args, fn) } - -// Arg returns the n'th command-line argument. Arg(0) is the first remaining -// argument after options have been processed. -func Arg(n int) string { - if n >= 0 && n < len(CommandLine.args) { - return CommandLine.args[n] - } - return "" -} - -// Arg returns the n'th argument. Arg(0) is the first remaining -// argument after options have been processed. -func (s *Set) Arg(n int) string { - if n >= 0 && n < len(s.args) { - return s.args[n] - } - return "" -} - -// Args returns the non-option command line arguments. -func Args() []string { - return CommandLine.args -} - -// Args returns the non-option arguments. -func (s *Set) Args() []string { - return s.args -} - -// NArgs returns the number of non-option command line arguments. -func NArgs() int { - return len(CommandLine.args) -} - -// NArgs returns the number of non-option arguments. -func (s *Set) NArgs() int { - return len(s.args) -} - -// SetParameters sets the parameters string for printing the command line -// usage. It defaults to "[parameters ...]" -func SetParameters(parameters string) { - CommandLine.parameters = parameters -} - -// SetParameters sets the parameters string for printing the s's usage. -// It defaults to "[parameters ...]" -func (s *Set) SetParameters(parameters string) { - s.parameters = parameters -} - -// Parameters returns the parameters set by SetParameters on s. -func (s *Set) Parameters() string { return s.parameters } - -// SetProgram sets the program name to program. Normally it is determined -// from the zeroth command line argument (see os.Args). -func SetProgram(program string) { - CommandLine.program = program -} - -// SetProgram sets s's program name to program. Normally it is determined -// from the zeroth argument passed to Getopt or Parse. -func (s *Set) SetProgram(program string) { - s.program = program -} - -// Program returns the program name associated with Set s. -func (s *Set) Program() string { return s.program } - -// SetUsage sets the function used by Parse to display the commands usage -// on error. It defaults to calling PrintUsage(os.Stderr). -func SetUsage(usage func()) { - CommandLine.usage = usage -} - -// SetUsage sets the function used by Parse to display usage on error. It -// defaults to calling f.PrintUsage(os.Stderr). -func (s *Set) SetUsage(usage func()) { - s.usage = usage -} - -// Lookup returns the Option associated with name. Name should either be -// a rune (the short name) or a string (the long name). -func Lookup(name interface{}) Option { - return CommandLine.Lookup(name) -} - -// Lookup returns the Option associated with name in s. Name should either be -// a rune (the short name) or a string (the long name). -func (s *Set) Lookup(name interface{}) Option { - switch v := name.(type) { - case rune: - return s.shortOptions[v] - case int: - return s.shortOptions[rune(v)] - case string: - return s.longOptions[v] - } - return nil -} - -// IsSet returns true if the Option associated with name was seen while -// parsing the command line arguments. Name should either be a rune (the -// short name) or a string (the long name). -func IsSet(name interface{}) bool { - return CommandLine.IsSet(name) -} - -// IsSet returns true if the Option associated with name was seen while -// parsing s. Name should either be a rune (the short name) or a string (the -// long name). -func (s *Set) IsSet(name interface{}) bool { - if opt := s.Lookup(name); opt != nil { - return opt.Seen() - } - return false -} - -// GetCount returns the number of times the Option associated with name has been -// seen while parsing the command line arguments. Name should either be a rune -// (the short name) or a string (the long name). -func GetCount(name interface{}) int { - return CommandLine.GetCount(name) -} - -// GetCount returns the number of times the Option associated with name has been -// seen while parsing s's arguments. Name should either be a rune (the short -// name) or a string (the long name). -func (s *Set) GetCount(name interface{}) int { - if opt := s.Lookup(name); opt != nil { - return opt.Count() - } - return 0 -} - -// GetValue returns the final value set to the command-line Option with name. -// If the option has not been seen while parsing s then the default value is -// returned. Name should either be a rune (the short name) or a string (the -// long name). -func GetValue(name interface{}) string { - return CommandLine.GetValue(name) -} - -// GetValue returns the final value set to the Option in s associated with name. -// If the option has not been seen while parsing s then the default value is -// returned. Name should either be a rune (the short name) or a string (the -// long name). -func (s *Set) GetValue(name interface{}) string { - if opt := s.Lookup(name); opt != nil { - return opt.String() - } - return "" -} - -// Visit visits the command-line options in lexicographical order, calling fn -// for each. It visits only those options that have been set. -func Visit(fn func(Option)) { CommandLine.Visit(fn) } - -// Visit visits the options in s in lexicographical order, calling fn -// for each. It visits only those options that have been set. -func (s *Set) Visit(fn func(Option)) { - sort.Sort(s.options) - for _, opt := range s.options { - if opt.count > 0 { - fn(opt) - } - } -} - -// VisitAll visits the options in s in lexicographical order, calling fn -// for each. It visits all options, even those not set. -func VisitAll(fn func(Option)) { CommandLine.VisitAll(fn) } - -// VisitAll visits the command-line flags in lexicographical order, calling fn -// for each. It visits all flags, even those not set. -func (s *Set) VisitAll(fn func(Option)) { - sort.Sort(s.options) - for _, opt := range s.options { - fn(opt) - } -} - -// Reset resets all the command line options to the initial state so it -// appears none of them have been seen. -func Reset() { - CommandLine.Reset() -} - -// Reset resets all the options in s to the initial state so it -// appears none of them have been seen. -func (s *Set) Reset() { - for _, opt := range s.options { - opt.Reset() - } -} diff --git a/v2/signed.go b/v2/signed.go deleted file mode 100644 index dc371a7..0000000 --- a/v2/signed.go +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2017 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -import ( - "fmt" - "strconv" - "sync" -) - -type signed int64 - -type SignedLimit struct { - Base int // Base for conversion as per strconv.ParseInt - Bits int // Number of bits as per strconv.ParseInt - Min int64 // Minimum allowed value if both Min and Max are not 0 - Max int64 // Maximum allowed value if both Min and Max are not 0 -} - -var ( - signedLimitsMu sync.Mutex - signedLimits = make(map[*signed]*SignedLimit) -) - -func (n *signed) Set(value string, opt Option) error { - signedLimitsMu.Lock() - l := signedLimits[n] - signedLimitsMu.Unlock() - if l == nil { - return fmt.Errorf("no limits defined for %s", opt.Name()) - } - v, err := strconv.ParseInt(value, l.Base, l.Bits) - if err != nil { - if e, ok := err.(*strconv.NumError); ok { - switch e.Err { - case strconv.ErrRange: - err = fmt.Errorf("value out of range: %s", value) - case strconv.ErrSyntax: - err = fmt.Errorf("not a valid number: %s", value) - } - } - return err - } - if l.Min != 0 || l.Max != 0 { - if v < l.Min { - return fmt.Errorf("value out of range (<%v): %s", l.Min, value) - } - if v > l.Max { - return fmt.Errorf("value out of range (>%v): %s", l.Max, value) - } - } - *n = signed(v) - return nil -} - -func (n *signed) String() string { - signedLimitsMu.Lock() - l := signedLimits[n] - signedLimitsMu.Unlock() - if l != nil && l.Base != 0 { - return strconv.FormatInt(int64(*n), l.Base) - } - return strconv.FormatInt(int64(*n), 10) -} - -// Signed creates an option that is stored in an int64 and is constrained -// by the limits pointed to by l. The Max and Min values are only used if -// at least one of the values are not 0. If Base is 0, the base is implied by -// the string's prefix: base 16 for "0x", base 8 for "0", and base 10 otherwise. -func Signed(name rune, value int64, l *SignedLimit, helpvalue ...string) *int64 { - CommandLine.signedOption(&value, "", name, l, helpvalue...) - return &value -} - -func (s *Set) Signed(name rune, value int64, l *SignedLimit, helpvalue ...string) *int64 { - s.signedOption(&value, "", name, l, helpvalue...) - return &value -} - -func SignedLong(name string, short rune, value int64, l *SignedLimit, helpvalue ...string) *int64 { - CommandLine.signedOption(&value, name, short, l, helpvalue...) - return &value -} - -func (s *Set) SignedLong(name string, short rune, value int64, l *SignedLimit, helpvalue ...string) *int64 { - s.signedOption(&value, name, short, l, helpvalue...) - return &value -} - -func (s *Set) signedOption(p *int64, name string, short rune, l *SignedLimit, helpvalue ...string) { - opt := s.FlagLong((*signed)(p), name, short, helpvalue...) - if l.Base > 36 || l.Base == 1 || l.Base < 0 { - fmt.Fprintf(stderr, "invalid base for %s: %d\n", opt.Name(), l.Base) - exit(1) - } - if l.Bits < 0 || l.Bits > 64 { - fmt.Fprintf(stderr, "invalid bit size for %s: %d\n", opt.Name(), l.Bits) - exit(1) - } - if l.Min > l.Max { - fmt.Fprintf(stderr, "min greater than max for %s\n", opt.Name()) - exit(1) - } - lim := *l - signedLimitsMu.Lock() - signedLimits[(*signed)(p)] = &lim - signedLimitsMu.Unlock() -} diff --git a/v2/signed_test.go b/v2/signed_test.go deleted file mode 100644 index 768a06d..0000000 --- a/v2/signed_test.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2017 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -import ( - "fmt" - "strings" - "testing" -) - -var signedNumberTests = []struct { - where string - in []string - l SignedLimit - out int64 - err string -}{ - { - where: loc(), - }, - { - loc(), - []string{"test", "-n", "1010"}, - SignedLimit{Base: 2, Bits: 5}, - 10, - "", - }, - { - loc(), - []string{"test", "-n", "1010"}, - SignedLimit{Base: 2, Bits: 4}, - 0, - "test: value out of range: 1010\n", - }, - { - loc(), - []string{"test", "-n", "-1000"}, - SignedLimit{Base: 2, Bits: 4}, - -8, - "", - }, - { - loc(), - []string{"test", "-n", "3"}, - SignedLimit{Min: 4, Max: 6}, - 0, - "test: value out of range (<4): 3\n", - }, - { - loc(), - []string{"test", "-n", "4"}, - SignedLimit{Min: 4, Max: 6}, - 4, - "", - }, - { - loc(), - []string{"test", "-n", "5"}, - SignedLimit{Min: 4, Max: 6}, - 5, - "", - }, - { - loc(), - []string{"test", "-n", "6"}, - SignedLimit{Min: 4, Max: 6}, - 6, - "", - }, - { - loc(), - []string{"test", "-n", "7"}, - SignedLimit{Min: 4, Max: 6}, - 0, - "test: value out of range (>6): 7\n", - }, -} - -func TestSigneds(t *testing.T) { - for x, tt := range signedNumberTests { - if strings.Index(tt.where, ":-") > 0 { - tt.where = fmt.Sprintf("#%d", x) - } - - reset() - n := Signed('n', 0, &tt.l) - parse(tt.in) - if s := checkError(tt.err); s != "" { - t.Errorf("%s: %s", tt.where, s) - } - if *n != tt.out { - t.Errorf("%s: got %v, want %v", tt.where, *n, tt.out) - } - } -} diff --git a/v2/string.go b/v2/string.go deleted file mode 100644 index cab5733..0000000 --- a/v2/string.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2017 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -// String returns a value option that stores is value as a string. The -// initial value of the string is passed in value. -func String(name rune, value string, helpvalue ...string) *string { - CommandLine.Flag(&value, name, helpvalue...) - return &value -} - -func (s *Set) String(name rune, value string, helpvalue ...string) *string { - s.Flag(&value, name, helpvalue...) - return &value -} - -func StringLong(name string, short rune, value string, helpvalue ...string) *string { - CommandLine.FlagLong(&value, name, short, helpvalue...) - return &value -} - -func (s *Set) StringLong(name string, short rune, value string, helpvalue ...string) *string { - s.FlagLong(&value, name, short, helpvalue...) - return &value -} diff --git a/v2/string_test.go b/v2/string_test.go deleted file mode 100644 index 31abae8..0000000 --- a/v2/string_test.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2017 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -import "testing" - -var stringTests = []struct { - where string - in []string - sout string - optout string - err string -}{ - { - loc(), - []string{}, - "one", - "two", - "", - }, - { - loc(), - []string{"test", "-s"}, - "one", - "two", - "test: missing parameter for -s\n", - }, - { - loc(), - []string{"test", "--opt"}, - "one", - "two", - "test: missing parameter for --opt\n", - }, - { - loc(), - []string{"test", "-svalue", "--opt=option"}, - "value", - "option", - "", - }, - { - loc(), - []string{"test", "-s", "value", "--opt", "option"}, - "value", - "option", - "", - }, - { - loc(), - []string{"test", "-swrong", "--opt=wrong", "-s", "value", "--opt", "option"}, - "value", - "option", - "", - }, -} - -func TestString(t *testing.T) { - for _, tt := range stringTests { - reset() - s := String('s', "one") - opt := StringLong("opt", 0, "two") - - parse(tt.in) - if s := checkError(tt.err); s != "" { - t.Errorf("%s: %s", tt.where, s) - } - if *s != tt.sout { - t.Errorf("%s: got s = %q, want %q", tt.where, *s, tt.sout) - } - if *opt != tt.optout { - t.Errorf("%s: got opt = %q, want %q", tt.where, *opt, tt.optout) - } - } -} diff --git a/v2/unsigned.go b/v2/unsigned.go deleted file mode 100644 index 5594660..0000000 --- a/v2/unsigned.go +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2017 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -import ( - "fmt" - "strconv" - "sync" -) - -type unsigned uint64 - -type UnsignedLimit struct { - Base int // Base for conversion as per strconv.ParseInt - Bits int // Number of bits as per strconv.ParseInt - Min uint64 // Minimum allowed value if both Min and Max are not 0 - Max uint64 // Maximum allowed value if both Min and Max are not 0 -} - -var ( - unsignedLimitsMu sync.Mutex - unsignedLimits = make(map[*unsigned]*UnsignedLimit) -) - -func (n *unsigned) Set(value string, opt Option) error { - unsignedLimitsMu.Lock() - l := unsignedLimits[n] - unsignedLimitsMu.Unlock() - if l == nil { - return fmt.Errorf("no limits defined for %s", opt.Name()) - } - v, err := strconv.ParseUint(value, l.Base, l.Bits) - if err != nil { - if e, ok := err.(*strconv.NumError); ok { - switch e.Err { - case strconv.ErrRange: - err = fmt.Errorf("value out of range: %s", value) - case strconv.ErrSyntax: - err = fmt.Errorf("not a valid number: %s", value) - } - } - return err - } - if l.Min != 0 || l.Max != 0 { - if v < l.Min { - return fmt.Errorf("value out of range (<%v): %s", l.Min, value) - } - if v > l.Max { - return fmt.Errorf("value out of range (>%v): %s", l.Max, value) - } - } - *n = unsigned(v) - return nil -} - -func (n *unsigned) String() string { - unsignedLimitsMu.Lock() - l := unsignedLimits[n] - unsignedLimitsMu.Unlock() - if l != nil && l.Base != 0 { - return strconv.FormatUint(uint64(*n), l.Base) - } - return strconv.FormatUint(uint64(*n), 10) -} - -// Unsigned creates an option that is stored in a uint64 and is -// constrained by the limits pointed to by l. The Max and Min values are only -// used if at least one of the values are not 0. If Base is 0, the base is -// implied by the string's prefix: base 16 for "0x", base 8 for "0", and base -// 10 otherwise. -func Unsigned(name rune, value uint64, l *UnsignedLimit, helpvalue ...string) *uint64 { - CommandLine.unsignedOption(&value, "", name, l, helpvalue...) - return &value -} - -func (s *Set) Unsigned(name rune, value uint64, l *UnsignedLimit, helpvalue ...string) *uint64 { - s.unsignedOption(&value, "", name, l, helpvalue...) - return &value -} - -func UnsignedLong(name string, short rune, value uint64, l *UnsignedLimit, helpvalue ...string) *uint64 { - CommandLine.unsignedOption(&value, name, short, l, helpvalue...) - return &value -} - -func (s *Set) UnsignedLong(name string, short rune, value uint64, l *UnsignedLimit, helpvalue ...string) *uint64 { - s.unsignedOption(&value, name, short, l, helpvalue...) - return &value -} - -func (s *Set) unsignedOption(p *uint64, name string, short rune, l *UnsignedLimit, helpvalue ...string) { - opt := s.FlagLong((*unsigned)(p), name, short, helpvalue...) - if l.Base > 36 || l.Base == 1 || l.Base < 0 { - fmt.Fprintf(stderr, "invalid base for %s: %d\n", opt.Name(), l.Base) - exit(1) - } - if l.Bits < 0 || l.Bits > 64 { - fmt.Fprintf(stderr, "invalid bit size for %s: %d\n", opt.Name(), l.Bits) - exit(1) - } - if l.Min > l.Max { - fmt.Fprintf(stderr, "min greater than max for %s\n", opt.Name()) - exit(1) - } - lim := *l - unsignedLimitsMu.Lock() - unsignedLimits[(*unsigned)(p)] = &lim - unsignedLimitsMu.Unlock() -} diff --git a/v2/unsigned_test.go b/v2/unsigned_test.go deleted file mode 100644 index bf3d61a..0000000 --- a/v2/unsigned_test.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2017 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -import ( - "fmt" - "strings" - "testing" -) - -var unsignedTests = []struct { - where string - in []string - l UnsignedLimit - out uint64 - err string -}{ - { - where: loc(), - }, - { - loc(), - []string{"test", "-n", "1010"}, - UnsignedLimit{Base: 2, Bits: 5}, - 10, - "", - }, - { - loc(), - []string{"test", "-n", "1010"}, - UnsignedLimit{Base: 2, Bits: 4}, - 10, - "", - }, - { - loc(), - []string{"test", "-n", "1010"}, - UnsignedLimit{Base: 2, Bits: 3}, - 0, - "test: value out of range: 1010\n", - }, - { - loc(), - []string{"test", "-n", "3"}, - UnsignedLimit{Min: 4, Max: 6}, - 0, - "test: value out of range (<4): 3\n", - }, - { - loc(), - []string{"test", "-n", "4"}, - UnsignedLimit{Min: 4, Max: 6}, - 4, - "", - }, - { - loc(), - []string{"test", "-n", "5"}, - UnsignedLimit{Min: 4, Max: 6}, - 5, - "", - }, - { - loc(), - []string{"test", "-n", "6"}, - UnsignedLimit{Min: 4, Max: 6}, - 6, - "", - }, - { - loc(), - []string{"test", "-n", "7"}, - UnsignedLimit{Min: 4, Max: 6}, - 0, - "test: value out of range (>6): 7\n", - }, -} - -func TestUnsigneds(t *testing.T) { - for x, tt := range unsignedTests { - if strings.Index(tt.where, ":-") > 0 { - tt.where = fmt.Sprintf("#%d", x) - } - - reset() - n := Unsigned('n', 0, &tt.l) - parse(tt.in) - if s := checkError(tt.err); s != "" { - t.Errorf("%s: %s", tt.where, s) - } - if *n != tt.out { - t.Errorf("%s: got %v, want %v", tt.where, *n, tt.out) - } - } -} diff --git a/v2/util_test.go b/v2/util_test.go deleted file mode 100644 index d46e198..0000000 --- a/v2/util_test.go +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2017 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -import ( - "bytes" - "fmt" - "path" - "reflect" - "runtime" - "strings" -) - -var errorString string - -func reset() { - CommandLine.shortOptions = make(map[rune]*option) - CommandLine.longOptions = make(map[string]*option) - CommandLine.options = nil - CommandLine.args = nil - CommandLine.program = "" - errorString = "" -} - -func parse(args []string) { - err := CommandLine.Getopt(args, nil) - if err != nil { - b := &bytes.Buffer{} - - fmt.Fprintln(b, CommandLine.program+": "+err.Error()) - CommandLine.PrintUsage(b) - errorString = b.String() - } -} - -func badSlice(a, b []string) bool { - if len(a) != len(b) { - return true - } - for x, v := range a { - if b[x] != v { - return true - } - } - return false -} - -func loc() string { - _, file, line, _ := runtime.Caller(1) - return fmt.Sprintf("%s:%d", path.Base(file), line) -} - -func (o *option) Equal(opt *option) bool { - if o.value != nil && opt.value == nil { - return false - } - if o.value == nil && opt.value != nil { - return false - } - if o.value != nil && o.value.String() != opt.value.String() { - return false - } - - oc := *o - optc := *opt - oc.value = nil - optc.value = nil - return reflect.DeepEqual(&oc, &optc) -} - -func checkError(err string) string { - switch { - case err == errorString: - return "" - case err == "": - return fmt.Sprintf("unexpected error %q", errorString) - case errorString == "": - return fmt.Sprintf("did not get expected error %q", err) - case !strings.HasPrefix(errorString, err): - return fmt.Sprintf("got error %q, want %q", errorString, err) - } - return "" -} diff --git a/v2/var.go b/v2/var.go deleted file mode 100644 index c60a9d2..0000000 --- a/v2/var.go +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2017 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -import ( - "fmt" - "runtime" - "strings" -) - -// Value is the interface to the dynamic value stored in a flag. Flags of type -// Value are declared using the Flag and FlagLong functions. -type Value interface { - // Set converts value into the appropriate type and assigns it to the - // receiver value. Option details are provided via opt (such as the - // flags name). - // - // Set is used to reset the value of an option to its default value - // (which is stored in string form internally). - Set(value string, opt Option) error - - // String returns the value of the flag as a string. - String() string -} - -var thisPackage string - -// init initializes thisPackage to our full package with the trailing . -// included. -func init() { - pc, _, _, ok := runtime.Caller(0) - if !ok { - return - } - f := runtime.FuncForPC(pc) - if f == nil { - return - } - thisPackage = f.Name() - x := strings.LastIndex(thisPackage, "/") - if x < 0 { - return - } - y := strings.Index(thisPackage[x:], ".") - if y < 0 { - return - } - // thisPackage includes the trailing . after the package name. - thisPackage = thisPackage[:x+y+1] -} - -// calledFrom returns a string containing the file and linenumber of the first -// stack frame above us that is not part of this package and is not a test. -// This is used to determine where a flag was initialized. -func calledFrom() string { - for i := 2; ; i++ { - pc, file, line, ok := runtime.Caller(i) - if !ok { - return "" - } - if !strings.HasSuffix(file, "_test.go") { - f := runtime.FuncForPC(pc) - if f != nil && strings.HasPrefix(f.Name(), thisPackage) { - continue - } - } - return fmt.Sprintf("%s:%d", file, line) - } -} - -func (s *Set) addFlag(p Value, name string, short rune, helpvalue ...string) Option { - opt := &option{ - short: short, - long: name, - value: p, - defval: p.String(), - } - - switch len(helpvalue) { - case 2: - opt.name = helpvalue[1] - fallthrough - case 1: - opt.help = helpvalue[0] - case 0: - default: - panic("Too many strings for String helpvalue") - } - if where := calledFrom(); where != "" { - opt.where = where - } - if opt.short == 0 && opt.long == "" { - fmt.Fprintf(stderr, opt.where+": no short or long option given") - exit(1) - } - s.AddOption(opt) - return opt -} From 53bd58b5ec0b4e4ba5618b37c8e7ea3da8528d65 Mon Sep 17 00:00:00 2001 From: Tim Otlik Date: Fri, 28 Jun 2019 23:23:55 +0200 Subject: [PATCH 2/4] refactor: remove v1 code, add v2 code for tagging --- bool.go | 62 ++---- bool_test.go | 2 +- breakup_test.go | 2 +- counter.go | 30 +-- counter_test.go | 17 +- duration.go | 44 +--- duration_test.go | 2 +- enum.go | 72 ++++--- enum_test.go | 17 +- error.go | 2 +- generic.go | 247 +++++++++++++++++++++++ generic_test.go | 319 +++++++++++++++++++++++++++++ getopt.go | 184 ++++++++++------- help_test.go | 145 ++++++++++++++ int.go | 168 ++++++++++++---- int16.go | 67 ------- int16_test.go | 84 -------- int32.go | 67 ------- int32_test.go | 84 -------- int64.go | 67 ------- int64_test.go | 84 -------- int_test.go | 513 ++++++++++++++++++++++++++++++++++++++++++++++- list.go | 57 +----- list_test.go | 4 +- option.go | 23 ++- set.go | 35 +++- signed.go | 42 ++-- signed_test.go | 2 +- string.go | 42 +--- string_test.go | 2 +- uint.go | 67 ------- uint16.go | 67 ------- uint16_test.go | 84 -------- uint32.go | 67 ------- uint32_test.go | 84 -------- uint64.go | 67 ------- uint64_test.go | 84 -------- uint_test.go | 84 -------- unsigned.go | 42 ++-- unsigned_test.go | 2 +- util_test.go | 4 +- var.go | 75 +++++-- 42 files changed, 1719 insertions(+), 1494 deletions(-) create mode 100644 generic.go create mode 100644 generic_test.go create mode 100644 help_test.go delete mode 100644 int16.go delete mode 100644 int16_test.go delete mode 100644 int32.go delete mode 100644 int32_test.go delete mode 100644 int64.go delete mode 100644 int64_test.go delete mode 100644 uint.go delete mode 100644 uint16.go delete mode 100644 uint16_test.go delete mode 100644 uint32.go delete mode 100644 uint32_test.go delete mode 100644 uint64.go delete mode 100644 uint64_test.go delete mode 100644 uint_test.go diff --git a/bool.go b/bool.go index 37ce019..2899bee 100644 --- a/bool.go +++ b/bool.go @@ -1,74 +1,36 @@ -// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2017 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package getopt -import ( - "fmt" - "strings" -) - -type boolValue bool - -func (b *boolValue) Set(value string, opt Option) error { - switch strings.ToLower(value) { - case "", "1", "true", "on", "t": - *b = true - case "0", "false", "off", "f": - *b = false - default: - return fmt.Errorf("invalid value for bool %s: %q", opt.Name(), value) - } - return nil -} - -func (b *boolValue) String() string { - if *b { - return "true" - } - return "false" -} - // Bool creates a flag option that is a bool. Bools normally do not take a // value however one can be assigned by using the long form of the option: // // --option=true // --o=false // -// Its value is case insenstive and one of true, false, t, f, on, off, t and 0. +// The value is case insensitive and one of true, false, t, f, on, off, t and 0. func Bool(name rune, helpvalue ...string) *bool { - return CommandLine.Bool(name, helpvalue...) + var b bool + CommandLine.Flag(&b, name, helpvalue...) + return &b } -func (s *Set) Bool(name rune, helpvalue ...string) *bool { +func BoolLong(name string, short rune, helpvalue ...string) *bool { var p bool - s.BoolVarLong(&p, "", name, helpvalue...) + CommandLine.FlagLong(&p, name, short, helpvalue...) return &p } -func BoolLong(name string, short rune, helpvalue ...string) *bool { - return CommandLine.BoolLong(name, short, helpvalue...) +func (s *Set) Bool(name rune, helpvalue ...string) *bool { + var b bool + s.Flag(&b, name, helpvalue...) + return &b } func (s *Set) BoolLong(name string, short rune, helpvalue ...string) *bool { var p bool - s.BoolVarLong(&p, name, short, helpvalue...) + s.FlagLong(&p, name, short, helpvalue...) return &p } - -func BoolVar(p *bool, name rune, helpvalue ...string) Option { - return CommandLine.BoolVar(p, name, helpvalue...) -} - -func (s *Set) BoolVar(p *bool, name rune, helpvalue ...string) Option { - return s.BoolVarLong(p, "", name, helpvalue...) -} - -func BoolVarLong(p *bool, name string, short rune, helpvalue ...string) Option { - return CommandLine.BoolVarLong(p, name, short, helpvalue...) -} - -func (s *Set) BoolVarLong(p *bool, name string, short rune, helpvalue ...string) Option { - return s.VarLong((*boolValue)(p), name, short, helpvalue...).SetFlag() -} diff --git a/bool_test.go b/bool_test.go index b112161..0a90d96 100644 --- a/bool_test.go +++ b/bool_test.go @@ -1,4 +1,4 @@ -// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2017 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/breakup_test.go b/breakup_test.go index c0ac148..5609e50 100644 --- a/breakup_test.go +++ b/breakup_test.go @@ -1,4 +1,4 @@ -// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2017 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/counter.go b/counter.go index 8b11ec1..ab10bc7 100644 --- a/counter.go +++ b/counter.go @@ -1,4 +1,4 @@ -// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2017 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -45,37 +45,25 @@ func (b *counterValue) String() string { // // Further instances of the option will increment from the set value. func Counter(name rune, helpvalue ...string) *int { - return CommandLine.Counter(name, helpvalue...) + var p int + CommandLine.FlagLong((*counterValue)(&p), "", name, helpvalue...).SetFlag() + return &p } func (s *Set) Counter(name rune, helpvalue ...string) *int { var p int - s.CounterVarLong(&p, "", name, helpvalue...) + s.FlagLong((*counterValue)(&p), "", name, helpvalue...).SetFlag() return &p } func CounterLong(name string, short rune, helpvalue ...string) *int { - return CommandLine.CounterLong(name, short, helpvalue...) + var p int + CommandLine.FlagLong((*counterValue)(&p), name, short, helpvalue...).SetFlag() + return &p } func (s *Set) CounterLong(name string, short rune, helpvalue ...string) *int { var p int - s.CounterVarLong(&p, name, short, helpvalue...) + s.FlagLong((*counterValue)(&p), name, short, helpvalue...).SetFlag() return &p } - -func CounterVar(p *int, name rune, helpvalue ...string) Option { - return CommandLine.CounterVar(p, name, helpvalue...) -} - -func (s *Set) CounterVar(p *int, name rune, helpvalue ...string) Option { - return s.CounterVarLong(p, "", name, helpvalue...) -} - -func CounterVarLong(p *int, name string, short rune, helpvalue ...string) Option { - return CommandLine.CounterVarLong(p, name, short, helpvalue...) -} - -func (s *Set) CounterVarLong(p *int, name string, short rune, helpvalue ...string) Option { - return s.VarLong((*counterValue)(p), name, short, helpvalue...).SetFlag() -} diff --git a/counter_test.go b/counter_test.go index 4b4e0cb..b85a26d 100644 --- a/counter_test.go +++ b/counter_test.go @@ -1,4 +1,4 @@ -// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2017 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -73,19 +73,4 @@ func TestCounter(t *testing.T) { t.Errorf("%s: got %v, want %v", tt.where, got, want) } } - - reset() - c := 5 - opt := CounterVar(&c, 'c') - parse([]string{"test", "-c"}) - if c != 6 { - t.Errorf("got %d, want 6", c) - } - if opt.Count() != 1 { - t.Errorf("got %d, want 1", c) - } - Reset() - if c != 5 { - t.Errorf("got %d, want 5", c) - } } diff --git a/duration.go b/duration.go index 9061113..7607815 100644 --- a/duration.go +++ b/duration.go @@ -1,4 +1,4 @@ -// Copyright 2015 Google Inc. All rights reserved. +// Copyright 2017 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -6,51 +6,23 @@ package getopt import "time" -type durationValue time.Duration - -func (d *durationValue) Set(value string, opt Option) error { - v, err := time.ParseDuration(value) - if err != nil { - return err - } - *d = durationValue(v) - return nil -} - -func (d *durationValue) String() string { - return time.Duration(*d).String() -} - // Duration creates an option that parses its value as a time.Duration. func Duration(name rune, value time.Duration, helpvalue ...string) *time.Duration { - return CommandLine.Duration(name, value, helpvalue...) + CommandLine.FlagLong(&value, "", name, helpvalue...) + return &value } func (s *Set) Duration(name rune, value time.Duration, helpvalue ...string) *time.Duration { - return s.DurationLong("", name, value, helpvalue...) + s.FlagLong(&value, "", name, helpvalue...) + return &value } func DurationLong(name string, short rune, value time.Duration, helpvalue ...string) *time.Duration { - return CommandLine.DurationLong(name, short, value, helpvalue...) + CommandLine.FlagLong(&value, name, short, helpvalue...) + return &value } func (s *Set) DurationLong(name string, short rune, value time.Duration, helpvalue ...string) *time.Duration { - s.DurationVarLong(&value, name, short, helpvalue...) + s.FlagLong(&value, name, short, helpvalue...) return &value } - -func DurationVar(p *time.Duration, name rune, helpvalue ...string) Option { - return CommandLine.DurationVar(p, name, helpvalue...) -} - -func (s *Set) DurationVar(p *time.Duration, name rune, helpvalue ...string) Option { - return s.DurationVarLong(p, "", name, helpvalue...) -} - -func DurationVarLong(p *time.Duration, name string, short rune, helpvalue ...string) Option { - return CommandLine.DurationVarLong(p, name, short, helpvalue...) -} - -func (s *Set) DurationVarLong(p *time.Duration, name string, short rune, helpvalue ...string) Option { - return s.VarLong((*durationValue)(p), name, short, helpvalue...) -} diff --git a/duration_test.go b/duration_test.go index fc4ded7..ffee177 100644 --- a/duration_test.go +++ b/duration_test.go @@ -1,4 +1,4 @@ -// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2017 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/enum.go b/enum.go index ee4cf95..1ca6ff9 100644 --- a/enum.go +++ b/enum.go @@ -1,17 +1,26 @@ -// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2017 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package getopt -import "errors" +import ( + "errors" + "fmt" + "sync" +) type enumValue string -var enumValues = make(map[*enumValue]map[string]struct{}) +var ( + enumValuesMu sync.Mutex + enumValues = make(map[*enumValue]map[string]struct{}) +) func (s *enumValue) Set(value string, opt Option) error { + enumValuesMu.Lock() es, ok := enumValues[s] + enumValuesMu.Unlock() if !ok || es == nil { return errors.New("this option has no values") } @@ -28,46 +37,43 @@ func (s *enumValue) String() string { // Enum creates an option that can only be set to one of the enumerated strings // passed in values. Passing nil or an empty slice results in an option that -// will always fail. -func Enum(name rune, values []string, helpvalue ...string) *string { - return CommandLine.Enum(name, values, helpvalue...) +// will always fail. If not "", value is the default value of the enum. If +// value is not listed in values then Enum will produce an error on standard +// error and then exit the program with a status of 1. +func Enum(name rune, values []string, value string, helpvalue ...string) *string { + return CommandLine.Enum(name, values, value, helpvalue...) } -func (s *Set) Enum(name rune, values []string, helpvalue ...string) *string { - var p string - s.EnumVarLong(&p, "", name, values, helpvalue...) - return &p +func (s *Set) Enum(name rune, values []string, value string, helpvalue ...string) *string { + var p enumValue + p.define(values, value, &option{short: name}) + s.FlagLong(&p, "", name, helpvalue...) + return (*string)(&p) } -func EnumLong(name string, short rune, values []string, helpvalue ...string) *string { - return CommandLine.EnumLong(name, short, values, helpvalue...) +func EnumLong(name string, short rune, values []string, value string, helpvalue ...string) *string { + return CommandLine.EnumLong(name, short, values, value, helpvalue...) } -func (s *Set) EnumLong(name string, short rune, values []string, helpvalue ...string) *string { - var p string - s.EnumVarLong(&p, name, short, values, helpvalue...) - return &p +func (s *Set) EnumLong(name string, short rune, values []string, value string, helpvalue ...string) *string { + var p enumValue + p.define(values, value, &option{short: short, long: name}) + s.FlagLong(&p, name, short, helpvalue...) + return (*string)(&p) } -// EnumVar creates an enum option that defaults to the starting value of *p. -// If *p is not found in values then a reset of this option will fail. -func EnumVar(p *string, name rune, values []string, helpvalue ...string) Option { - return CommandLine.EnumVar(p, name, values, helpvalue...) -} - -func (s *Set) EnumVar(p *string, name rune, values []string, helpvalue ...string) Option { - return s.EnumVarLong(p, "", name, values, helpvalue...) -} - -func EnumVarLong(p *string, name string, short rune, values []string, helpvalue ...string) Option { - return CommandLine.EnumVarLong(p, name, short, values, helpvalue...) -} - -func (s *Set) EnumVarLong(p *string, name string, short rune, values []string, helpvalue ...string) Option { +func (e *enumValue) define(values []string, def string, opt Option) { m := make(map[string]struct{}) for _, v := range values { m[v] = struct{}{} } - enumValues[(*enumValue)(p)] = m - return s.VarLong((*enumValue)(p), name, short, helpvalue...) + enumValuesMu.Lock() + enumValues[e] = m + enumValuesMu.Unlock() + if def != "" { + if err := e.Set(def, nil); err != nil { + fmt.Fprintf(stderr, "setting default for %s: %v\n", opt.Name(), err) + exit(1) + } + } } diff --git a/enum_test.go b/enum_test.go index 3c04ee8..c854015 100644 --- a/enum_test.go +++ b/enum_test.go @@ -1,4 +1,4 @@ -// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2017 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -14,6 +14,7 @@ var enumTests = []struct { where string in []string values []string + def string out string err string }{ @@ -23,11 +24,13 @@ var enumTests = []struct { []string{}, "", "", + "", }, { loc(), []string{"test", "-e", "val1"}, []string{"val1", "val2"}, + "", "val1", "", }, @@ -35,6 +38,15 @@ var enumTests = []struct { loc(), []string{"test", "-e", "val1", "-e", "val2"}, []string{"val1", "val2"}, + "", + "val2", + "", + }, + { + loc(), + []string{"test"}, + []string{"val1", "val2"}, + "val2", "val2", "", }, @@ -43,6 +55,7 @@ var enumTests = []struct { []string{"test", "-e", "val3"}, []string{"val1", "val2"}, "", + "", "test: invalid value: val3\n", }, } @@ -54,7 +67,7 @@ func TestEnum(t *testing.T) { } reset() - e := Enum('e', tt.values) + e := Enum('e', tt.values, tt.def) parse(tt.in) if s := checkError(tt.err); s != "" { t.Errorf("%s: %s", tt.where, s) diff --git a/error.go b/error.go index 3de8e86..6f668a3 100644 --- a/error.go +++ b/error.go @@ -1,4 +1,4 @@ -// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2017 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/generic.go b/generic.go new file mode 100644 index 0000000..2cead19 --- /dev/null +++ b/generic.go @@ -0,0 +1,247 @@ +// Copyright 2017 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package getopt + +import ( + "fmt" + "strconv" + "strings" + "time" +) + +type generic struct { + p interface{} +} + +// Flag is shorthand for CommandLine.Flag. +func Flag(v interface{}, short rune, helpvalue ...string) Option { + return CommandLine.long(v, "", short, helpvalue...) +} + +// FlagLong is shorthand for CommandLine.LongFlag. +func FlagLong(v interface{}, long string, short rune, helpvalue ...string) Option { + return CommandLine.long(v, long, short, helpvalue...) +} + +// Flag calls FlagLong with only a short flag name. +func (s *Set) Flag(v interface{}, short rune, helpvalue ...string) Option { + return s.long(v, "", short, helpvalue...) +} + +// FlagLong returns an Option in Set s for setting v. If long is not "" then +// the option has a long name, and if short is not 0, the option has a short +// name. v must either be of type getopt.Value or a pointer to one of the +// supported builtin types: +// +// bool, string, []string +// int, int8, int16, int32, int64 +// uint, uint8, uint16, uint32, uint64 +// float32, float64 +// time.Duration +// +// FlagLong will panic if v is not a getopt.Value or one of the supported +// builtin types. +// +// The default value of the flag is the value of v at the time FlagLong is +// called. +func (s *Set) FlagLong(v interface{}, long string, short rune, helpvalue ...string) Option { + return s.long(v, long, short, helpvalue...) +} + +func (s *Set) long(v interface{}, long string, short rune, helpvalue ...string) (opt Option) { + // Fix up our location when we return. + if where := calledFrom(); where != "" { + defer func() { + if opt, ok := opt.(*option); ok { + opt.where = where + } + }() + } + switch p := v.(type) { + case Value: + return s.addFlag(p, long, short, helpvalue...) + case *bool: + return s.addFlag(&generic{v}, long, short, helpvalue...).SetFlag() + case *string, *[]string: + return s.addFlag(&generic{v}, long, short, helpvalue...) + case *int, *int8, *int16, *int32, *int64: + return s.addFlag(&generic{v}, long, short, helpvalue...) + case *uint, *uint8, *uint16, *uint32, *uint64: + return s.addFlag(&generic{v}, long, short, helpvalue...) + case *float32, *float64: + return s.addFlag(&generic{v}, long, short, helpvalue...) + case *time.Duration: + return s.addFlag(&generic{v}, long, short, helpvalue...) + default: + panic(fmt.Sprintf("unsupported flag type: %T", v)) + } +} + +// genericValue returns the object underlying the generic Value v, or nil if v +// is not a generic Value. +func genericValue(v Value) interface{} { + if g, ok := v.(*generic); ok { + return g.p + } + return nil +} + +func (g *generic) Set(value string, opt Option) error { + strconvErr := func(err error) error { + if e, ok := err.(*strconv.NumError); ok { + switch e.Err { + case strconv.ErrRange: + err = fmt.Errorf("value out of range: %s", value) + case strconv.ErrSyntax: + err = fmt.Errorf("not a valid number: %s", value) + } + } + return err + } + switch p := g.p.(type) { + case *bool: + switch strings.ToLower(value) { + case "", "1", "true", "on", "t": + *p = true + case "0", "false", "off", "f": + *p = false + default: + return fmt.Errorf("invalid value for bool %s: %q", opt.Name(), value) + } + return nil + case *string: + *p = value + return nil + case *[]string: + a := strings.Split(value, ",") + // If this is the first time we are seen then nil out the + // default value. + if opt.Count() <= 1 { + *p = nil + } + *p = append(*p, a...) + return nil + case *int: + i64, err := strconv.ParseInt(value, 0, strconv.IntSize) + if err == nil { + *p = int(i64) + } + return strconvErr(err) + case *int8: + i64, err := strconv.ParseInt(value, 0, 8) + if err == nil { + *p = int8(i64) + } + return strconvErr(err) + case *int16: + i64, err := strconv.ParseInt(value, 0, 16) + if err == nil { + *p = int16(i64) + } + return strconvErr(err) + case *int32: + i64, err := strconv.ParseInt(value, 0, 32) + if err == nil { + *p = int32(i64) + } + return strconvErr(err) + case *int64: + i64, err := strconv.ParseInt(value, 0, 64) + if err == nil { + *p = i64 + } + return strconvErr(err) + case *uint: + u64, err := strconv.ParseUint(value, 0, strconv.IntSize) + if err == nil { + *p = uint(u64) + } + return strconvErr(err) + case *uint8: + u64, err := strconv.ParseUint(value, 0, 8) + if err == nil { + *p = uint8(u64) + } + return strconvErr(err) + case *uint16: + u64, err := strconv.ParseUint(value, 0, 16) + if err == nil { + *p = uint16(u64) + } + return strconvErr(err) + case *uint32: + u64, err := strconv.ParseUint(value, 0, 32) + if err == nil { + *p = uint32(u64) + } + return strconvErr(err) + case *uint64: + u64, err := strconv.ParseUint(value, 0, 64) + if err == nil { + *p = u64 + } + return strconvErr(err) + case *float32: + f64, err := strconv.ParseFloat(value, 32) + if err == nil { + *p = float32(f64) + } + return strconvErr(err) + case *float64: + f64, err := strconv.ParseFloat(value, 64) + if err == nil { + *p = f64 + } + return strconvErr(err) + case *time.Duration: + v, err := time.ParseDuration(value) + if err == nil { + *p = v + } + return err + } + panic("internal error") +} + +func (g *generic) String() string { + switch p := g.p.(type) { + case *bool: + if *p { + return "true" + } + return "false" + case *string: + return *p + case *[]string: + return strings.Join([]string(*p), ",") + case *int: + return strconv.FormatInt(int64(*p), 10) + case *int8: + return strconv.FormatInt(int64(*p), 10) + case *int16: + return strconv.FormatInt(int64(*p), 10) + case *int32: + return strconv.FormatInt(int64(*p), 10) + case *int64: + return strconv.FormatInt(*p, 10) + case *uint: + return strconv.FormatUint(uint64(*p), 10) + case *uint8: + return strconv.FormatUint(uint64(*p), 10) + case *uint16: + return strconv.FormatUint(uint64(*p), 10) + case *uint32: + return strconv.FormatUint(uint64(*p), 10) + case *uint64: + return strconv.FormatUint(*p, 10) + case *float32: + return strconv.FormatFloat(float64(*p), 'g', -1, 32) + case *float64: + return strconv.FormatFloat(*p, 'g', -1, 64) + case *time.Duration: + return p.String() + } + panic("internal error") +} diff --git a/generic_test.go b/generic_test.go new file mode 100644 index 0000000..9a247ec --- /dev/null +++ b/generic_test.go @@ -0,0 +1,319 @@ +// Copyright 2017 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package getopt + +import ( + "bytes" + "fmt" + "os" + "reflect" + "runtime" + "strings" + "testing" + "time" +) + +func TestGeneric(t *testing.T) { + const ( + shortTest = iota + longTest + bothTest + ) + for _, tt := range []struct { + where string + kind int + val interface{} + str string + def interface{} + in []string + err string + }{ + // Do all four tests for string, the rest can mostly just use + // shortTest (the 0 value). + { + where: loc(), + kind: shortTest, + val: "42", + str: "42", + in: []string{"test", "-s", "42"}, + }, + { + where: loc(), + kind: longTest, + val: "42", + str: "42", + in: []string{"test", "--long", "42"}, + }, + { + where: loc(), + kind: bothTest, + val: "42", + str: "42", + in: []string{"test", "--both", "42"}, + }, + { + where: loc(), + kind: bothTest, + val: "42", + str: "42", + in: []string{"test", "-b", "42"}, + }, + { + where: loc(), + val: "42", + def: "42", + str: "42", + in: []string{"test"}, + }, + { + where: loc(), + val: "42", + def: "43", + str: "42", + in: []string{"test", "-s", "42"}, + }, + + { + where: loc(), + val: true, + str: "true", + in: []string{"test", "-s"}, + }, + { + where: loc(), + val: true, + def: true, + str: "true", + in: []string{"test"}, + }, + { + where: loc(), + kind: longTest, + val: false, + str: "false", + in: []string{"test", "--long=false"}, + }, + { + where: loc(), + kind: longTest, + val: false, + def: true, + str: "false", + in: []string{"test", "--long=false"}, + }, + + { + where: loc(), + val: int(42), + str: "42", + in: []string{"test", "-s", "42"}, + }, + { + where: loc(), + val: int8(42), + str: "42", + in: []string{"test", "-s", "42"}, + }, + { + where: loc(), + val: int16(42), + str: "42", + in: []string{"test", "-s", "42"}, + }, + { + where: loc(), + val: int32(42), + str: "42", + in: []string{"test", "-s", "42"}, + }, + { + where: loc(), + val: int64(42), + str: "42", + in: []string{"test", "-s", "42"}, + }, + + { + where: loc(), + val: uint(42), + str: "42", + in: []string{"test", "-s", "42"}, + }, + { + where: loc(), + val: uint8(42), + str: "42", + in: []string{"test", "-s", "42"}, + }, + { + where: loc(), + val: uint16(42), + str: "42", + in: []string{"test", "-s", "42"}, + }, + { + where: loc(), + val: uint32(42), + str: "42", + in: []string{"test", "-s", "42"}, + }, + { + where: loc(), + val: uint64(42), + str: "42", + in: []string{"test", "-s", "42"}, + }, + + { + where: loc(), + val: float32(4.2), + str: "4.2", + in: []string{"test", "-s", "4.2"}, + }, + { + where: loc(), + val: float64(4.2), + str: "4.2", + in: []string{"test", "-s", "4.2"}, + }, + + { + where: loc(), + val: time.Duration(time.Second * 42), + def: time.Second * 2, + str: "42s", + in: []string{"test", "-s", "42s"}, + }, + { + where: loc(), + val: time.Duration(time.Second * 42), + def: time.Second * 2, + str: "42s", + in: []string{"test", "-s42s"}, + }, + { + where: loc(), + val: time.Duration(time.Second * 2), + def: time.Second * 2, + in: []string{"test", "-s42"}, + str: "2s", + err: "test: time: missing unit in duration 42", + }, + + { + where: loc(), + val: []string{"42", "."}, + str: "42,.", + def: []string{"one", "two", "three"}, + in: []string{"test", "-s42", "-s."}, + }, + { + where: loc(), + val: []string{"42", "."}, + str: "42,.", + def: []string{"one", "two", "three"}, + in: []string{"test", "-s42,."}, + }, + { + where: loc(), + val: []string{"one", "two", "three"}, + def: []string{"one", "two", "three"}, + str: "one,two,three", + in: []string{"test"}, + }, + } { + reset() + var opt Option + val := reflect.New(reflect.TypeOf(tt.val)).Interface() + if tt.def != nil { + reflect.ValueOf(val).Elem().Set(reflect.ValueOf(tt.def)) + } + switch tt.kind { + case shortTest: + opt = Flag(val, 's') + case longTest: + opt = FlagLong(val, "long", 0) + case bothTest: + opt = FlagLong(val, "both", 'b') + } + _ = opt + parse(tt.in) + if s := checkError(tt.err); s != "" { + t.Errorf("%s: %s", tt.where, s) + continue + } + got := reflect.ValueOf(val).Elem().Interface() + want := reflect.ValueOf(tt.val).Interface() + if !reflect.DeepEqual(got, want) { + t.Errorf("%s: got %v, want %v", tt.where, got, want) + } + if str := opt.String(); str != tt.str { + t.Errorf("%s: got string %q, want %q", tt.where, str, tt.str) + } + } +} + +func TestGenericDup(t *testing.T) { + defer func() { + stderr = os.Stderr + exit = os.Exit + }() + + reset() + var v1, v2 string + type myPanic struct{} + var errbuf bytes.Buffer + stderr = &errbuf + _, file, line, _ := runtime.Caller(0) + Flag(&v1, 's') + line++ // line is now the line number of the first call to Flag. + + exit = func(i int) { panic(myPanic{}) } + defer func() { + p := recover() + if _, ok := p.(myPanic); ok { + err := errbuf.String() + if !strings.Contains(err, "-s already declared") || !strings.Contains(err, fmt.Sprintf("%s:%d", file, line)) { + t.Errorf("unexpected error: %q\nshould contain \"-s already declared\" and \"%s:%d\"", err, file, line) + } + } else if p == nil { + t.Errorf("Second call to Flag did not fail") + } else { + t.Errorf("panic %v", p) + } + }() + Flag(&v2, 's') +} + +func TestGenericDupNested(t *testing.T) { + defer func() { + stderr = os.Stderr + exit = os.Exit + }() + + reset() + type myPanic struct{} + var errbuf bytes.Buffer + stderr = &errbuf + _, file, line, _ := runtime.Caller(0) + String('s', "default") + line++ // line is now the line number of the first call to Flag. + + exit = func(i int) { panic(myPanic{}) } + defer func() { + p := recover() + if _, ok := p.(myPanic); ok { + err := errbuf.String() + if !strings.Contains(err, "-s already declared") || !strings.Contains(err, fmt.Sprintf("%s:%d", file, line)) { + t.Errorf("unexpected error: %q\nshould contain \"-s already declared\" and \"%s:%d\"", err, file, line) + } + } else if p == nil { + t.Errorf("Second call to Flag did not fail") + } else { + t.Errorf("panic %v", p) + } + }() + String('s', "default") +} diff --git a/getopt.go b/getopt.go index 755500b..1ffbfaa 100644 --- a/getopt.go +++ b/getopt.go @@ -1,16 +1,16 @@ -// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2017 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package getopt (v1) provides traditional getopt processing for implementing +// Package getopt (v2) provides traditional getopt processing for implementing // commands that use traditional command lines. The standard Go flag package // cannot be used to write a program that parses flags the way ls or ssh does, -// for example. +// for example. Version 2 of this package has a simplified API. // -// A new version of this package (v2) (whose package name is also getopt) is -// available as: +// See the github.com/pborman/options package for a simple structure based +// interface to this package. // -// "github.com/pborman/getopt/v2" +// USAGE // // Getopt supports functionality found in both the standard BSD getopt as well // as (one of the many versions of) the GNU getopt_long. Being a Go package, @@ -19,15 +19,28 @@ // // Typical usage: // -// // Declare the flags to be used +// // Declare flags and have getopt return pointers to the values. // helpFlag := getopt.Bool('?', "display help") -// cmdFlag := getopt.StringLong("command", 'c', "", "the command") +// cmdFlag := getopt.StringLong("command", 'c', "default", "the command") +// +// // Declare flags against existing variables. +// var { +// fileName = "/the/default/path" +// timeout = time.Second * 5 +// verbose bool +// } +// func init() { +// getopt.Flag(&verbose, 'v', "be verbose") +// getopt.FlagLong(&fileName, "path", 0, "the path") +// getopt.FlagLong(&timeout, "timeout", 't', "some timeout") +// } // // func main() { // // Parse the program arguments // getopt.Parse() // // Get the remaining positional parameters // args := getopt.Args() +// ... // // If you don't want the program to exit on error, use getopt.Getopt: // @@ -37,6 +50,8 @@ // fmt.Fprintln(os.Stderr, err) // } // +// FLAG SYNTAX +// // Support is provided for both short (-f) and long (--flag) options. A single // option may have both a short and a long name. Each option may be a flag or a // value. A value takes an argument. @@ -75,14 +90,14 @@ // --val -f (sets v and f as being seen) // --val=value -f (sets v to "value" and sets f) // -// There is no convience function defined for making the value optional. The +// There is no convenience function defined for making the value optional. The // SetOptional method must be called on the actual Option. // // v := String("val", 'v', "", "the optional v") // Lookup("v").SetOptional() // // var s string -// StringVar(&s, "val", 'v', "the optional v).SetOptional() +// FlagLong(&s, "val", 'v', "the optional v).SetOptional() // // Parsing continues until the first non-option or "--" is encountered. // @@ -92,6 +107,8 @@ // then the single "-" will also terminate the option processing but unlike // "--", the "-" will be part of the remaining arguments. // +// ADVANCED USAGE +// // Normally the parsing is performed by calling the Parse function. If it is // important to see the order of the options then the Getopt function should be // used. The standard Parse function does the equivalent of: @@ -113,8 +130,8 @@ // error is encountered then Args will begin with argument that caused the // error. // -// It is valid to call a set's Parse a second time to amend the current set of -// flags or values. As an example: +// It is valid to call a set's Parse a second time to amen flags or values. As +// an example: // // var a = getopt.Bool('a', "", "The a flag") // var b = getopt.Bool('b', "", "The a flag") @@ -135,58 +152,27 @@ // Unless an option type explicitly prohibits it, an option may appear more than // once in the arguments. The last value provided to the option is the value. // -// SYNTAX -// -// For each option type there are an unfortunately large number of ways, 8, to -// initialize the option. This number is derived from three attributes: -// -// 1) Short or Long name -// 2) Normal vs Var -// 3) Command Line vs Option Set -// -// The first two variations provide 4 signature: -// -// Option(name rune, [value type,] helpvalue... string) -// OptionLong(name string, short rune, [value type,] helpvalue... string) -// OptionVar(p *type, name rune, helpvalue... string) -// OptionVarLong(p *type, name string, short rune, helpvalue... string) +// BUILTIN TYPES // -// Foo can actually be expressed in terms of FooLong: +// The Flag and FlagLong functions support most standard Go types. For the +// list, see the description of FlagLong below for a list of supported types. // -// func Foo(name rune, value type, helpvalue... string) *type { -// return FooLong("", name, value, helpvalue...) -// } -// -// Normally Foo is used, unless long options are needed. Setting short to 0 -// creates only a long option. -// -// The difference bentween Foo and FooVar is that you pass a pointer, p, to the -// location of the value to FooVar. The default value is simply *p. The -// initial value of *p is the defaut value of the option. +// There are also helper routines to allow single line flag declarations. These +// types are: Bool, Counter, Duration, Enum, Int16, Int32, Int64, Int, List, +// Signed, String, Uint16, Uint32, Uint64, Uint, and Unsigned. // -// Foo is actually a wrapper around FooVar: -// -// func Foo(name rune, value type, helpvalue... string) *type { -// p := value -// FooVar(&p, name, helpvalue... string) -// return &p -// } +// Each comes in a short and long flavor, e.g., Bool and BoolLong and include +// functions to set the flags on the standard command line or for a specific Set +// of flags. // +// Except for the Counter, Enum, Signed and Unsigned types, all of these types +// can be declared using Flag and FlagLong by passing in a pointer to the +// appropriate type. // -// The third variation provides a top-level function and a method on a Set: -// -// func Option(...) -// func (s *Set) Option(...) -// -// The top-level function is simply: -// -// func Option(...) *type { -// return CommandLine.Option(...) { -// } +// DECLARING NEW FLAG TYPES // -// To simplfy documentation, typically only the main top-level function is fully -// documented. The others will have documentation when there is something -// special about them. +// A pointer to any type that implements the Value interface may be passed to +// Flag or FlagLong. // // VALUEHELP // @@ -218,6 +204,7 @@ import ( "path" "sort" "strings" + "time" ) // stderr allows tests to capture output to standard error. @@ -238,8 +225,24 @@ var DisplayWidth = 80 // the u flag's usage is quite long var HelpColumn = 20 -// PrintUsage prints the usage of the program to w. +// PrintUsage prints the usage line and set of options of set S to w. func (s *Set) PrintUsage(w io.Writer) { + parts := make([]string, 2, 4) + parts[0] = "Usage:" + parts[1] = s.program + if usage := s.UsageLine(); usage != "" { + parts = append(parts, usage) + } + if s.parameters != "" { + parts = append(parts, s.parameters) + } + fmt.Fprintln(w, strings.Join(parts, " ")) + s.PrintOptions(w) +} + +// UsageLine returns the usage line for the set s. The set's program name and +// parameters, if any, are not included. +func (s *Set) UsageLine() string { sort.Sort(s.options) flags := "" @@ -291,13 +294,9 @@ func (s *Set) PrintUsage(w io.Writer) { } flags = strings.Join(opts, "] [") if flags != "" { - flags = " [" + flags + "]" - } - if s.parameters != "" { - flags += " " + s.parameters + flags = "[" + flags + "]" } - fmt.Fprintf(w, "Usage: %s%s\n", s.program, flags) - s.PrintOptions(w) + return flags } // PrintOptions prints the list of options in s to w. @@ -323,7 +322,36 @@ func (s *Set) PrintOptions(w io.Writer) { fmt.Fprintf(w, " %s\n", opt.uname) continue } - help := strings.Split(opt.help, "\n") + helpMsg := opt.help + + // If the default value is the known zero value + // then don't display it. + def := opt.defval + switch genericValue(opt.value).(type) { + case *bool: + if def == "false" { + def = "" + } + case *int, *int8, *int16, *int32, *int64, + *uint, *uint8, *uint16, *uint32, *uint64, + *float32, *float64: + if def == "0" { + def = "" + } + case *time.Duration: + if def == "0s" { + def = "" + } + default: + if opt.flag && def == "false" { + def = "" + } + } + if def != "" { + helpMsg += " [" + def + "]" + } + + help := strings.Split(helpMsg, "\n") // If they did not put in newlines then we will insert // them to keep the help messages from wrapping. if len(help) == 1 { @@ -400,19 +428,19 @@ func (s *Set) Parse(args []string) { // Getopt returns nil when all options have been processed (a non-option // argument was encountered, "--" was encountered, or fn returned false). // -// On error getopt returns a refernce to an InvalidOption (which implements -// the error interface). +// On error getopt returns a reference to an InvalidOption (which implements the +// error interface). func (s *Set) Getopt(args []string, fn func(Option) bool) (err error) { - s.State = InProgress + s.setState(InProgress) defer func() { - if s.State == InProgress { + if s.State() == InProgress { switch { case err != nil: - s.State = Failure + s.setState(Failure) case len(s.args) == 0: - s.State = EndOfArguments + s.setState(EndOfArguments) default: - s.State = Unknown + s.setState(Unknown) } } }() @@ -435,7 +463,7 @@ Parsing: // end of options? if arg == "" || arg[0] != '-' { - s.State = EndOfOptions + s.setState(EndOfOptions) return nil } @@ -446,7 +474,7 @@ Parsing: // explicitly request end of options? if arg == "--" { s.args = args - s.State = DashDash + s.setState(DashDash) return nil } @@ -485,7 +513,7 @@ Parsing: } if !fn(opt) { - s.State = Terminated + s.setState(Terminated) return nil } continue Parsing @@ -501,7 +529,7 @@ Parsing: // as an option, a lone - is treated as // if there were a -- in front of it. if arg == "-" { - s.State = Dash + s.setState(Dash) return nil } return unknownOption(c) @@ -523,7 +551,7 @@ Parsing: return setError(opt, value, err) } if !fn(opt) { - s.State = Terminated + s.setState(Terminated) return nil } if !opt.flag { diff --git a/help_test.go b/help_test.go new file mode 100644 index 0000000..c7b5bb0 --- /dev/null +++ b/help_test.go @@ -0,0 +1,145 @@ +package getopt + +import ( + "bytes" + "fmt" + "strings" + "testing" + "time" +) + +type flagValue bool + +func (f *flagValue) Set(value string, opt Option) error { + switch strings.ToLower(value) { + case "true", "t", "on", "1": + *f = true + case "false", "f", "off", "0": + *f = false + default: + return fmt.Errorf("invalid flagValue %q", value) + } + return nil +} +func (f *flagValue) String() string { + return fmt.Sprint(bool(*f)) +} + +func TestHelpDefaults(t *testing.T) { + HelpColumn = 40 + set := New() + bf := false + bt := true + set.FlagLong(&bf, "bool_false", 'f', "false bool value") + set.FlagLong(&bt, "bool_true", 't', "true bool value") + i := int(0) + i8 := int8(0) + i16 := int16(0) + i32 := int32(0) + i64 := int64(0) + si := int(1) + si8 := int8(8) + si16 := int16(16) + si32 := int32(32) + si64 := int64(64) + ui := uint(0) + ui8 := uint8(0) + ui16 := uint16(0) + ui32 := uint32(0) + ui64 := uint64(0) + sui := uint(1) + sui8 := uint8(8) + sui16 := uint16(16) + sui32 := uint32(32) + sui64 := uint64(64) + + set.FlagLong(&i, "int", 0, "int value") + set.FlagLong(&si, "int_set", 0, "set int value") + set.FlagLong(&i8, "int8", 0, "int8 value") + set.FlagLong(&si8, "int8_set", 0, "set int8 value") + set.FlagLong(&i16, "int16", 0, "int16 value") + set.FlagLong(&si16, "int16_set", 0, "set int16 value") + set.FlagLong(&i32, "int32", 0, "int32 value") + set.FlagLong(&si32, "int32_set", 0, "set int32 value") + set.FlagLong(&i64, "int64", 0, "int64 value") + set.FlagLong(&si64, "int64_set", 0, "set int64 value") + + set.FlagLong(&ui, "uint", 0, "uint value") + set.FlagLong(&sui, "uint_set", 0, "set uint value") + set.FlagLong(&ui8, "uint8", 0, "uint8 value") + set.FlagLong(&sui8, "uint8_set", 0, "set uint8 value") + set.FlagLong(&ui16, "uint16", 0, "uint16 value") + set.FlagLong(&sui16, "uint16_set", 0, "set uint16 value") + set.FlagLong(&ui32, "uint32", 0, "uint32 value") + set.FlagLong(&sui32, "uint32_set", 0, "set uint32 value") + set.FlagLong(&ui64, "uint64", 0, "uint64 value") + set.FlagLong(&sui64, "uint64_set", 0, "set uint64 value") + + f32 := float32(0) + f64 := float64(0) + sf32 := float32(3.2) + sf64 := float64(6.4) + + set.FlagLong(&f32, "float32", 0, "float32 value") + set.FlagLong(&sf32, "float32_set", 0, "set float32 value") + set.FlagLong(&f64, "float64", 0, "float64 value") + set.FlagLong(&sf64, "float64_set", 0, "set float64 value") + + d := time.Duration(0) + sd := time.Duration(time.Second) + + set.FlagLong(&d, "duration", 0, "duration value") + set.FlagLong(&sd, "duration_set", 0, "set duration value") + + str := "" + sstr := "string" + set.FlagLong(&str, "string", 0, "string value") + set.FlagLong(&sstr, "string_set", 0, "set string value") + + var fv flagValue + set.FlagLong(&fv, "vbool", 0, "value bool").SetFlag() + + var fvo flagValue = true + set.FlagLong(&fvo, "vbool_on", 0, "value bool").SetFlag() + + want := ` + --duration=value duration value + --duration_set=value set duration value [1s] + -f, --bool_false false bool value + --float32=value float32 value + --float32_set=value set float32 value [3.2] + --float64=value float64 value + --float64_set=value set float64 value [6.4] + --int=value int value + --int16=value int16 value + --int16_set=value set int16 value [16] + --int32=value int32 value + --int32_set=value set int32 value [32] + --int64=value int64 value + --int64_set=value set int64 value [64] + --int8=value int8 value + --int8_set=value set int8 value [8] + --int_set=value set int value [1] + --string=value string value + --string_set=value set string value [string] + -t, --bool_true true bool value [true] + --uint=value uint value + --uint16=value uint16 value + --uint16_set=value set uint16 value [16] + --uint32=value uint32 value + --uint32_set=value set uint32 value [32] + --uint64=value uint64 value + --uint64_set=value set uint64 value [64] + --uint8=value uint8 value + --uint8_set=value set uint8 value [8] + --uint_set=value set uint value [1] + --vbool value bool + --vbool_on value bool [true] +`[1:] + + var buf bytes.Buffer + set.PrintOptions(&buf) + if got := buf.String(); got != want { + t.Errorf("got:\n%s\nwant:\n%s", got, want) + } +} diff --git a/int.go b/int.go index 3db5fd6..42833c1 100644 --- a/int.go +++ b/int.go @@ -1,44 +1,17 @@ -// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2017 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package getopt -import ( - "fmt" - "strconv" -) - -type intValue int - -func (i *intValue) Set(value string, opt Option) error { - v, err := strconv.ParseInt(value, 0, strconv.IntSize) - if err != nil { - if e, ok := err.(*strconv.NumError); ok { - switch e.Err { - case strconv.ErrRange: - err = fmt.Errorf("value out of range: %s", value) - case strconv.ErrSyntax: - err = fmt.Errorf("not a valid number: %s", value) - } - } - return err - } - *i = intValue(v) - return nil -} - -func (i *intValue) String() string { - return strconv.FormatInt(int64(*i), 10) -} - // Int creates an option that parses its value as an integer. func Int(name rune, value int, helpvalue ...string) *int { return CommandLine.Int(name, value, helpvalue...) } func (s *Set) Int(name rune, value int, helpvalue ...string) *int { - return s.IntLong("", name, value, helpvalue...) + s.Flag(&value, name, helpvalue...) + return &value } func IntLong(name string, short rune, value int, helpvalue ...string) *int { @@ -46,22 +19,139 @@ func IntLong(name string, short rune, value int, helpvalue ...string) *int { } func (s *Set) IntLong(name string, short rune, value int, helpvalue ...string) *int { - s.IntVarLong(&value, name, short, helpvalue...) + s.FlagLong(&value, name, short, helpvalue...) + return &value +} + +// Int16 creates an option that parses its value as a 16 bit integer. +func Int16(name rune, value int16, helpvalue ...string) *int16 { + return CommandLine.Int16(name, value, helpvalue...) +} + +func (s *Set) Int16(name rune, value int16, helpvalue ...string) *int16 { + s.Flag(&value, name, helpvalue...) return &value } -func IntVar(p *int, name rune, helpvalue ...string) Option { - return CommandLine.IntVar(p, name, helpvalue...) +func Int16Long(name string, short rune, value int16, helpvalue ...string) *int16 { + return CommandLine.Int16Long(name, short, value, helpvalue...) } -func (s *Set) IntVar(p *int, name rune, helpvalue ...string) Option { - return s.IntVarLong(p, "", name, helpvalue...) +func (s *Set) Int16Long(name string, short rune, value int16, helpvalue ...string) *int16 { + s.FlagLong(&value, name, short, helpvalue...) + return &value +} + +// Int32 creates an option that parses its value as a 32 bit integer. +func Int32(name rune, value int32, helpvalue ...string) *int32 { + return CommandLine.Int32(name, value, helpvalue...) +} + +func (s *Set) Int32(name rune, value int32, helpvalue ...string) *int32 { + s.Flag(&value, name, helpvalue...) + return &value } -func IntVarLong(p *int, name string, short rune, helpvalue ...string) Option { - return CommandLine.IntVarLong(p, name, short, helpvalue...) +func Int32Long(name string, short rune, value int32, helpvalue ...string) *int32 { + return CommandLine.Int32Long(name, short, value, helpvalue...) } -func (s *Set) IntVarLong(p *int, name string, short rune, helpvalue ...string) Option { - return s.VarLong((*intValue)(p), name, short, helpvalue...) +func (s *Set) Int32Long(name string, short rune, value int32, helpvalue ...string) *int32 { + s.FlagLong(&value, name, short, helpvalue...) + return &value +} + +// Int64 creates an option that parses its value as a 64 bit integer. +func Int64(name rune, value int64, helpvalue ...string) *int64 { + return CommandLine.Int64(name, value, helpvalue...) +} + +func (s *Set) Int64(name rune, value int64, helpvalue ...string) *int64 { + s.Flag(&value, name, helpvalue...) + return &value +} + +func Int64Long(name string, short rune, value int64, helpvalue ...string) *int64 { + return CommandLine.Int64Long(name, short, value, helpvalue...) +} + +func (s *Set) Int64Long(name string, short rune, value int64, helpvalue ...string) *int64 { + s.FlagLong(&value, name, short, helpvalue...) + return &value +} + +// Uint creates an option that parses its value as an unsigned integer. +func Uint(name rune, value uint, helpvalue ...string) *uint { + return CommandLine.Uint(name, value, helpvalue...) +} + +func (s *Set) Uint(name rune, value uint, helpvalue ...string) *uint { + s.Flag(&value, name, helpvalue...) + return &value +} + +func UintLong(name string, short rune, value uint, helpvalue ...string) *uint { + return CommandLine.UintLong(name, short, value, helpvalue...) +} + +func (s *Set) UintLong(name string, short rune, value uint, helpvalue ...string) *uint { + s.FlagLong(&value, name, short, helpvalue...) + return &value +} + +// Uint16 creates an option that parses its value as a 16 bit unsigned integer. +func Uint16(name rune, value uint16, helpvalue ...string) *uint16 { + return CommandLine.Uint16(name, value, helpvalue...) +} + +func (s *Set) Uint16(name rune, value uint16, helpvalue ...string) *uint16 { + s.Flag(&value, name, helpvalue...) + return &value +} + +func Uint16Long(name string, short rune, value uint16, helpvalue ...string) *uint16 { + return CommandLine.Uint16Long(name, short, value, helpvalue...) +} + +func (s *Set) Uint16Long(name string, short rune, value uint16, helpvalue ...string) *uint16 { + s.FlagLong(&value, name, short, helpvalue...) + return &value +} + +// Uint32 creates an option that parses its value as a 32 bit unsigned integer. +func Uint32(name rune, value uint32, helpvalue ...string) *uint32 { + return CommandLine.Uint32(name, value, helpvalue...) +} + +func (s *Set) Uint32(name rune, value uint32, helpvalue ...string) *uint32 { + s.Flag(&value, name, helpvalue...) + return &value +} + +func Uint32Long(name string, short rune, value uint32, helpvalue ...string) *uint32 { + return CommandLine.Uint32Long(name, short, value, helpvalue...) +} + +func (s *Set) Uint32Long(name string, short rune, value uint32, helpvalue ...string) *uint32 { + s.FlagLong(&value, name, short, helpvalue...) + return &value +} + +// Uint64 creates an option that parses its value as a 64 bit unsigned integer. +func Uint64(name rune, value uint64, helpvalue ...string) *uint64 { + return CommandLine.Uint64(name, value, helpvalue...) +} + +func (s *Set) Uint64(name rune, value uint64, helpvalue ...string) *uint64 { + s.Flag(&value, name, helpvalue...) + return &value +} + +func Uint64Long(name string, short rune, value uint64, helpvalue ...string) *uint64 { + return CommandLine.Uint64Long(name, short, value, helpvalue...) +} + +func (s *Set) Uint64Long(name string, short rune, value uint64, helpvalue ...string) *uint64 { + s.FlagLong(&value, name, short, helpvalue...) + return &value } diff --git a/int16.go b/int16.go deleted file mode 100644 index 2ece51f..0000000 --- a/int16.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2013 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -import ( - "fmt" - "strconv" -) - -type int16Value int16 - -func (i *int16Value) Set(value string, opt Option) error { - v, err := strconv.ParseInt(value, 0, 16) - if err != nil { - if e, ok := err.(*strconv.NumError); ok { - switch e.Err { - case strconv.ErrRange: - err = fmt.Errorf("value out of range: %s", value) - case strconv.ErrSyntax: - err = fmt.Errorf("not a valid number: %s", value) - } - } - return err - } - *i = int16Value(v) - return nil -} - -func (i *int16Value) String() string { - return strconv.FormatInt(int64(*i), 10) -} - -// Int16 creates an option that parses its value as an int16. -func Int16(name rune, value int16, helpvalue ...string) *int16 { - return CommandLine.Int16(name, value, helpvalue...) -} - -func (s *Set) Int16(name rune, value int16, helpvalue ...string) *int16 { - return s.Int16Long("", name, value, helpvalue...) -} - -func Int16Long(name string, short rune, value int16, helpvalue ...string) *int16 { - return CommandLine.Int16Long(name, short, value, helpvalue...) -} - -func (s *Set) Int16Long(name string, short rune, value int16, helpvalue ...string) *int16 { - s.Int16VarLong(&value, name, short, helpvalue...) - return &value -} - -func Int16Var(p *int16, name rune, helpvalue ...string) Option { - return CommandLine.Int16Var(p, name, helpvalue...) -} - -func (s *Set) Int16Var(p *int16, name rune, helpvalue ...string) Option { - return s.Int16VarLong(p, "", name, helpvalue...) -} - -func Int16VarLong(p *int16, name string, short rune, helpvalue ...string) Option { - return CommandLine.Int16VarLong(p, name, short, helpvalue...) -} - -func (s *Set) Int16VarLong(p *int16, name string, short rune, helpvalue ...string) Option { - return s.VarLong((*int16Value)(p), name, short, helpvalue...) -} diff --git a/int16_test.go b/int16_test.go deleted file mode 100644 index 8b83c1e..0000000 --- a/int16_test.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2013 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -import ( - "fmt" - "strings" - "testing" -) - -var int16Tests = []struct { - where string - in []string - i int16 - int16 int16 - err string -}{ - { - loc(), - []string{}, - 17, 42, - "", - }, - { - loc(), - []string{"test", "-i", "1", "--int16", "2"}, - 1, 2, - "", - }, - { - loc(), - []string{"test", "-i1", "--int16=2"}, - 1, 2, - "", - }, - { - loc(), - []string{"test", "-i1", "-i2"}, - 2, 42, - "", - }, - { - loc(), - []string{"test", "-i=1"}, - 17, 42, - "test: not a valid number: =1\n", - }, - { - loc(), - []string{"test", "-i0x20"}, - 0x20, 42, - "", - }, - { - loc(), - []string{"test", "-i010"}, - 8, 42, - "", - }, -} - -func TestInt16(t *testing.T) { - for x, tt := range int16Tests { - reset() - i := Int16('i', 17) - opt := Int16Long("int16", 0, 42) - if strings.Index(tt.where, ":-") > 0 { - tt.where = fmt.Sprintf("#%d", x) - } - - parse(tt.in) - if s := checkError(tt.err); s != "" { - t.Errorf("%s: %s", tt.where, s) - } - if got, want := *i, tt.i; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - if got, want := *opt, tt.int16; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - } -} diff --git a/int32.go b/int32.go deleted file mode 100644 index b8f3415..0000000 --- a/int32.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2013 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -import ( - "fmt" - "strconv" -) - -type int32Value int32 - -func (i *int32Value) Set(value string, opt Option) error { - v, err := strconv.ParseInt(value, 0, 32) - if err != nil { - if e, ok := err.(*strconv.NumError); ok { - switch e.Err { - case strconv.ErrRange: - err = fmt.Errorf("value out of range: %s", value) - case strconv.ErrSyntax: - err = fmt.Errorf("not a valid number: %s", value) - } - } - return err - } - *i = int32Value(v) - return nil -} - -func (i *int32Value) String() string { - return strconv.FormatInt(int64(*i), 10) -} - -// Int32 creates an option that parses its value as an int32. -func Int32(name rune, value int32, helpvalue ...string) *int32 { - return CommandLine.Int32(name, value, helpvalue...) -} - -func (s *Set) Int32(name rune, value int32, helpvalue ...string) *int32 { - return s.Int32Long("", name, value, helpvalue...) -} - -func Int32Long(name string, short rune, value int32, helpvalue ...string) *int32 { - return CommandLine.Int32Long(name, short, value, helpvalue...) -} - -func (s *Set) Int32Long(name string, short rune, value int32, helpvalue ...string) *int32 { - s.Int32VarLong(&value, name, short, helpvalue...) - return &value -} - -func Int32Var(p *int32, name rune, helpvalue ...string) Option { - return CommandLine.Int32Var(p, name, helpvalue...) -} - -func (s *Set) Int32Var(p *int32, name rune, helpvalue ...string) Option { - return s.Int32VarLong(p, "", name, helpvalue...) -} - -func Int32VarLong(p *int32, name string, short rune, helpvalue ...string) Option { - return CommandLine.Int32VarLong(p, name, short, helpvalue...) -} - -func (s *Set) Int32VarLong(p *int32, name string, short rune, helpvalue ...string) Option { - return s.VarLong((*int32Value)(p), name, short, helpvalue...) -} diff --git a/int32_test.go b/int32_test.go deleted file mode 100644 index 9da9aeb..0000000 --- a/int32_test.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2013 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -import ( - "fmt" - "strings" - "testing" -) - -var int32Tests = []struct { - where string - in []string - i int32 - int32 int32 - err string -}{ - { - loc(), - []string{}, - 17, 42, - "", - }, - { - loc(), - []string{"test", "-i", "1", "--int32", "2"}, - 1, 2, - "", - }, - { - loc(), - []string{"test", "-i1", "--int32=2"}, - 1, 2, - "", - }, - { - loc(), - []string{"test", "-i1", "-i2"}, - 2, 42, - "", - }, - { - loc(), - []string{"test", "-i=1"}, - 17, 42, - "test: not a valid number: =1\n", - }, - { - loc(), - []string{"test", "-i0x20"}, - 0x20, 42, - "", - }, - { - loc(), - []string{"test", "-i010"}, - 8, 42, - "", - }, -} - -func TestInt32(t *testing.T) { - for x, tt := range int32Tests { - reset() - i := Int32('i', 17) - opt := Int32Long("int32", 0, 42) - if strings.Index(tt.where, ":-") > 0 { - tt.where = fmt.Sprintf("#%d", x) - } - - parse(tt.in) - if s := checkError(tt.err); s != "" { - t.Errorf("%s: %s", tt.where, s) - } - if got, want := *i, tt.i; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - if got, want := *opt, tt.int32; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - } -} diff --git a/int64.go b/int64.go deleted file mode 100644 index d29e6b4..0000000 --- a/int64.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2013 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -import ( - "fmt" - "strconv" -) - -type int64Value int64 - -func (i *int64Value) Set(value string, opt Option) error { - v, err := strconv.ParseInt(value, 0, 64) - if err != nil { - if e, ok := err.(*strconv.NumError); ok { - switch e.Err { - case strconv.ErrRange: - err = fmt.Errorf("value out of range: %s", value) - case strconv.ErrSyntax: - err = fmt.Errorf("not a valid number: %s", value) - } - } - return err - } - *i = int64Value(v) - return nil -} - -func (i *int64Value) String() string { - return strconv.FormatInt(int64(*i), 10) -} - -// Int64 creates an option that parses its value as an int64. -func Int64(name rune, value int64, helpvalue ...string) *int64 { - return CommandLine.Int64(name, value, helpvalue...) -} - -func (s *Set) Int64(name rune, value int64, helpvalue ...string) *int64 { - return s.Int64Long("", name, value, helpvalue...) -} - -func Int64Long(name string, short rune, value int64, helpvalue ...string) *int64 { - return CommandLine.Int64Long(name, short, value, helpvalue...) -} - -func (s *Set) Int64Long(name string, short rune, value int64, helpvalue ...string) *int64 { - s.Int64VarLong(&value, name, short, helpvalue...) - return &value -} - -func Int64Var(p *int64, name rune, helpvalue ...string) Option { - return CommandLine.Int64Var(p, name, helpvalue...) -} - -func (s *Set) Int64Var(p *int64, name rune, helpvalue ...string) Option { - return s.Int64VarLong(p, "", name, helpvalue...) -} - -func Int64VarLong(p *int64, name string, short rune, helpvalue ...string) Option { - return CommandLine.Int64VarLong(p, name, short, helpvalue...) -} - -func (s *Set) Int64VarLong(p *int64, name string, short rune, helpvalue ...string) Option { - return s.VarLong((*int64Value)(p), name, short, helpvalue...) -} diff --git a/int64_test.go b/int64_test.go deleted file mode 100644 index 7fa01b5..0000000 --- a/int64_test.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2013 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -import ( - "fmt" - "strings" - "testing" -) - -var int64Tests = []struct { - where string - in []string - i int64 - int64 int64 - err string -}{ - { - loc(), - []string{}, - 17, 42, - "", - }, - { - loc(), - []string{"test", "-i", "1", "--int64", "2"}, - 1, 2, - "", - }, - { - loc(), - []string{"test", "-i1", "--int64=2"}, - 1, 2, - "", - }, - { - loc(), - []string{"test", "-i1", "-i2"}, - 2, 42, - "", - }, - { - loc(), - []string{"test", "-i=1"}, - 17, 42, - "test: not a valid number: =1\n", - }, - { - loc(), - []string{"test", "-i0x20"}, - 0x20, 42, - "", - }, - { - loc(), - []string{"test", "-i010"}, - 8, 42, - "", - }, -} - -func TestInt64(t *testing.T) { - for x, tt := range int64Tests { - reset() - i := Int64('i', 17) - opt := Int64Long("int64", 0, 42) - if strings.Index(tt.where, ":-") > 0 { - tt.where = fmt.Sprintf("#%d", x) - } - - parse(tt.in) - if s := checkError(tt.err); s != "" { - t.Errorf("%s: %s", tt.where, s) - } - if got, want := *i, tt.i; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - if got, want := *opt, tt.int64; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - } -} diff --git a/int_test.go b/int_test.go index f12af31..57175fd 100644 --- a/int_test.go +++ b/int_test.go @@ -1,4 +1,4 @@ -// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2017 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -82,3 +82,514 @@ func TestInt(t *testing.T) { } } } + +var int16Tests = []struct { + where string + in []string + i int16 + int16 int16 + err string +}{ + { + loc(), + []string{}, + 17, 42, + "", + }, + { + loc(), + []string{"test", "-i", "1", "--int16", "2"}, + 1, 2, + "", + }, + { + loc(), + []string{"test", "-i1", "--int16=2"}, + 1, 2, + "", + }, + { + loc(), + []string{"test", "-i1", "-i2"}, + 2, 42, + "", + }, + { + loc(), + []string{"test", "-i=1"}, + 17, 42, + "test: not a valid number: =1\n", + }, + { + loc(), + []string{"test", "-i0x20"}, + 0x20, 42, + "", + }, + { + loc(), + []string{"test", "-i010"}, + 8, 42, + "", + }, +} + +func TestInt16(t *testing.T) { + for x, tt := range int16Tests { + reset() + i := Int16('i', 17) + opt := Int16Long("int16", 0, 42) + if strings.Index(tt.where, ":-") > 0 { + tt.where = fmt.Sprintf("#%d", x) + } + + parse(tt.in) + if s := checkError(tt.err); s != "" { + t.Errorf("%s: %s", tt.where, s) + } + if got, want := *i, tt.i; got != want { + t.Errorf("%s: got %v, want %v", tt.where, got, want) + } + if got, want := *opt, tt.int16; got != want { + t.Errorf("%s: got %v, want %v", tt.where, got, want) + } + } +} + +var int32Tests = []struct { + where string + in []string + i int32 + int32 int32 + err string +}{ + { + loc(), + []string{}, + 17, 42, + "", + }, + { + loc(), + []string{"test", "-i", "1", "--int32", "2"}, + 1, 2, + "", + }, + { + loc(), + []string{"test", "-i1", "--int32=2"}, + 1, 2, + "", + }, + { + loc(), + []string{"test", "-i1", "-i2"}, + 2, 42, + "", + }, + { + loc(), + []string{"test", "-i=1"}, + 17, 42, + "test: not a valid number: =1\n", + }, + { + loc(), + []string{"test", "-i0x20"}, + 0x20, 42, + "", + }, + { + loc(), + []string{"test", "-i010"}, + 8, 42, + "", + }, +} + +func TestInt32(t *testing.T) { + for x, tt := range int32Tests { + reset() + i := Int32('i', 17) + opt := Int32Long("int32", 0, 42) + if strings.Index(tt.where, ":-") > 0 { + tt.where = fmt.Sprintf("#%d", x) + } + + parse(tt.in) + if s := checkError(tt.err); s != "" { + t.Errorf("%s: %s", tt.where, s) + } + if got, want := *i, tt.i; got != want { + t.Errorf("%s: got %v, want %v", tt.where, got, want) + } + if got, want := *opt, tt.int32; got != want { + t.Errorf("%s: got %v, want %v", tt.where, got, want) + } + } +} + +var int64Tests = []struct { + where string + in []string + i int64 + int64 int64 + err string +}{ + { + loc(), + []string{}, + 17, 42, + "", + }, + { + loc(), + []string{"test", "-i", "1", "--int64", "2"}, + 1, 2, + "", + }, + { + loc(), + []string{"test", "-i1", "--int64=2"}, + 1, 2, + "", + }, + { + loc(), + []string{"test", "-i1", "-i2"}, + 2, 42, + "", + }, + { + loc(), + []string{"test", "-i=1"}, + 17, 42, + "test: not a valid number: =1\n", + }, + { + loc(), + []string{"test", "-i0x20"}, + 0x20, 42, + "", + }, + { + loc(), + []string{"test", "-i010"}, + 8, 42, + "", + }, +} + +func TestInt64(t *testing.T) { + for x, tt := range int64Tests { + reset() + i := Int64('i', 17) + opt := Int64Long("int64", 0, 42) + if strings.Index(tt.where, ":-") > 0 { + tt.where = fmt.Sprintf("#%d", x) + } + + parse(tt.in) + if s := checkError(tt.err); s != "" { + t.Errorf("%s: %s", tt.where, s) + } + if got, want := *i, tt.i; got != want { + t.Errorf("%s: got %v, want %v", tt.where, got, want) + } + if got, want := *opt, tt.int64; got != want { + t.Errorf("%s: got %v, want %v", tt.where, got, want) + } + } +} + +var uintTests = []struct { + where string + in []string + i uint + uint uint + err string +}{ + { + loc(), + []string{}, + 17, 42, + "", + }, + { + loc(), + []string{"test", "-i", "1", "--uint", "2"}, + 1, 2, + "", + }, + { + loc(), + []string{"test", "-i1", "--uint=2"}, + 1, 2, + "", + }, + { + loc(), + []string{"test", "-i1", "-i2"}, + 2, 42, + "", + }, + { + loc(), + []string{"test", "-i=1"}, + 17, 42, + "test: not a valid number: =1\n", + }, + { + loc(), + []string{"test", "-i0x20"}, + 0x20, 42, + "", + }, + { + loc(), + []string{"test", "-i010"}, + 8, 42, + "", + }, +} + +func TestUint(t *testing.T) { + for x, tt := range uintTests { + reset() + i := Uint('i', 17) + opt := UintLong("uint", 0, 42) + if strings.Index(tt.where, ":-") > 0 { + tt.where = fmt.Sprintf("#%d", x) + } + + parse(tt.in) + if s := checkError(tt.err); s != "" { + t.Errorf("%s: %s", tt.where, s) + } + if got, want := *i, tt.i; got != want { + t.Errorf("%s: got %v, want %v", tt.where, got, want) + } + if got, want := *opt, tt.uint; got != want { + t.Errorf("%s: got %v, want %v", tt.where, got, want) + } + } +} + +var uint16Tests = []struct { + where string + in []string + i uint16 + uint16 uint16 + err string +}{ + { + loc(), + []string{}, + 17, 42, + "", + }, + { + loc(), + []string{"test", "-i", "1", "--uint16", "2"}, + 1, 2, + "", + }, + { + loc(), + []string{"test", "-i1", "--uint16=2"}, + 1, 2, + "", + }, + { + loc(), + []string{"test", "-i1", "-i2"}, + 2, 42, + "", + }, + { + loc(), + []string{"test", "-i=1"}, + 17, 42, + "test: not a valid number: =1\n", + }, + { + loc(), + []string{"test", "-i0x20"}, + 0x20, 42, + "", + }, + { + loc(), + []string{"test", "-i010"}, + 8, 42, + "", + }, +} + +func TestUint16(t *testing.T) { + for x, tt := range uint16Tests { + reset() + i := Uint16('i', 17) + opt := Uint16Long("uint16", 0, 42) + if strings.Index(tt.where, ":-") > 0 { + tt.where = fmt.Sprintf("#%d", x) + } + + parse(tt.in) + if s := checkError(tt.err); s != "" { + t.Errorf("%s: %s", tt.where, s) + } + if got, want := *i, tt.i; got != want { + t.Errorf("%s: got %v, want %v", tt.where, got, want) + } + if got, want := *opt, tt.uint16; got != want { + t.Errorf("%s: got %v, want %v", tt.where, got, want) + } + } +} + +var uint32Tests = []struct { + where string + in []string + i uint32 + uint32 uint32 + err string +}{ + { + loc(), + []string{}, + 17, 42, + "", + }, + { + loc(), + []string{"test", "-i", "1", "--uint32", "2"}, + 1, 2, + "", + }, + { + loc(), + []string{"test", "-i1", "--uint32=2"}, + 1, 2, + "", + }, + { + loc(), + []string{"test", "-i1", "-i2"}, + 2, 42, + "", + }, + { + loc(), + []string{"test", "-i=1"}, + 17, 42, + "test: not a valid number: =1\n", + }, + { + loc(), + []string{"test", "-i0x20"}, + 0x20, 42, + "", + }, + { + loc(), + []string{"test", "-i010"}, + 8, 42, + "", + }, +} + +func TestUint32(t *testing.T) { + for x, tt := range uint32Tests { + reset() + i := Uint32('i', 17) + opt := Uint32Long("uint32", 0, 42) + if strings.Index(tt.where, ":-") > 0 { + tt.where = fmt.Sprintf("#%d", x) + } + + parse(tt.in) + if s := checkError(tt.err); s != "" { + t.Errorf("%s: %s", tt.where, s) + } + if got, want := *i, tt.i; got != want { + t.Errorf("%s: got %v, want %v", tt.where, got, want) + } + if got, want := *opt, tt.uint32; got != want { + t.Errorf("%s: got %v, want %v", tt.where, got, want) + } + } +} + +var uint64Tests = []struct { + where string + in []string + i uint64 + uint64 uint64 + err string +}{ + { + loc(), + []string{}, + 17, 42, + "", + }, + { + loc(), + []string{"test", "-i", "1", "--uint64", "2"}, + 1, 2, + "", + }, + { + loc(), + []string{"test", "-i1", "--uint64=2"}, + 1, 2, + "", + }, + { + loc(), + []string{"test", "-i1", "-i2"}, + 2, 42, + "", + }, + { + loc(), + []string{"test", "-i=1"}, + 17, 42, + "test: not a valid number: =1\n", + }, + { + loc(), + []string{"test", "-i0x20"}, + 0x20, 42, + "", + }, + { + loc(), + []string{"test", "-i010"}, + 8, 42, + "", + }, +} + +func TestUint64(t *testing.T) { + for x, tt := range uint64Tests { + reset() + i := Uint64('i', 17) + opt := Uint64Long("uint64", 0, 42) + if strings.Index(tt.where, ":-") > 0 { + tt.where = fmt.Sprintf("#%d", x) + } + + parse(tt.in) + if s := checkError(tt.err); s != "" { + t.Errorf("%s: %s", tt.where, s) + } + if got, want := *i, tt.i; got != want { + t.Errorf("%s: got %v, want %v", tt.where, got, want) + } + if got, want := *opt, tt.uint64; got != want { + t.Errorf("%s: got %v, want %v", tt.where, got, want) + } + } +} diff --git a/list.go b/list.go index b9d4267..99a09ef 100644 --- a/list.go +++ b/list.go @@ -1,69 +1,32 @@ -// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2017 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package getopt -import "strings" - -type listValue []string - -func (s *listValue) Set(value string, opt Option) error { - a := strings.Split(value, ",") - // If this is the first time we are seen then nil out the - // default value. - if opt.Count() <= 1 { - *s = nil - } - *s = append(*s, a...) - return nil -} - -func (s *listValue) String() string { - return strings.Join([]string(*s), ",") -} - // List creates an option that returns a slice of strings. The parameters -// passed are converted from a comma seperated value list into a slice. +// passed are converted from a comma separated value list into a slice. // Subsequent occurrences append to the list. func List(name rune, helpvalue ...string) *[]string { - return CommandLine.List(name, helpvalue...) + p := []string{} + CommandLine.Flag(&p, name, helpvalue...) + return &p } func (s *Set) List(name rune, helpvalue ...string) *[]string { p := []string{} - s.ListVar(&p, name, helpvalue...) + s.Flag(&p, name, helpvalue...) return &p } func ListLong(name string, short rune, helpvalue ...string) *[]string { - return CommandLine.ListLong(name, short, helpvalue...) + p := []string{} + CommandLine.FlagLong(&p, name, short, helpvalue...) + return &p } func (s *Set) ListLong(name string, short rune, helpvalue ...string) *[]string { p := []string{} - s.ListVarLong(&p, name, short, helpvalue...) + s.FlagLong(&p, name, short, helpvalue...) return &p } - -// ListVar creats a list option and places the values in p. If p is pointing -// to a list of values then those are considered the default values. The first -// time name is seen in the options the list will be set to list specified by -// the parameter to the option. Subsequent instances of the option will append -// to the list. -func ListVar(p *[]string, name rune, helpvalue ...string) Option { - return CommandLine.ListVar(p, name, helpvalue...) -} - -func (s *Set) ListVar(p *[]string, name rune, helpvalue ...string) Option { - return s.ListVarLong(p, "", name, helpvalue...) -} - -func ListVarLong(p *[]string, name string, short rune, helpvalue ...string) Option { - return CommandLine.ListVarLong(p, name, short, helpvalue...) -} - -func (s *Set) ListVarLong(p *[]string, name string, short rune, helpvalue ...string) Option { - opt := s.VarLong((*listValue)(p), name, short, helpvalue...) - return opt -} diff --git a/list_test.go b/list_test.go index 71f9c32..e8c8d18 100644 --- a/list_test.go +++ b/list_test.go @@ -1,4 +1,4 @@ -// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2017 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -72,7 +72,7 @@ func TestList(t *testing.T) { func TestDefaultList(t *testing.T) { reset() list := []string{"d1", "d2", "d3"} - ListVar(&list, 'l') + Flag(&list, 'l') parse([]string{"test"}) want := []string{"d1", "d2", "d3"} diff --git a/option.go b/option.go index 17dbe58..67822bc 100644 --- a/option.go +++ b/option.go @@ -1,4 +1,4 @@ -// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2017 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -17,6 +17,14 @@ type Option interface { // as a string, else it will be the long name. Name() string + // ShortName always returns the short name of the option, or "" if there + // is no short name. The name does not include the "-". + ShortName() string + + // LongName always returns the long name of the option, or "" if there + // is no long name. The name does not include the "--". + LongName() string + // IsFlag returns true if Option is a flag. IsFlag() bool @@ -43,7 +51,7 @@ type Option interface { // SetFlag makes the value a flag. Flags are boolean values and // normally do not taken a value. They are set to true when seen. // If a value is passed in the long form then it must be on, case - // insenstive, one of "true", "false", "t", "f", "on", "off", "1", "0". + // insensitivinsensitive, one of "true", "false", "t", "f", "on", "off", "1", "0". // SetFlag returns the Option SetFlag() Option @@ -134,6 +142,17 @@ func (o *option) Name() string { return "--" + o.long } +func (o *option) ShortName() string { + if o.short != 0 { + return string(o.short) + } + return "" +} + +func (o *option) LongName() string { + return o.long +} + // Reset rests an option so that it appears it has not yet been seen. func (o *option) Reset() { o.isLong = false diff --git a/set.go b/set.go index 64d9ecf..a7895c8 100644 --- a/set.go +++ b/set.go @@ -1,4 +1,4 @@ -// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2017 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -8,9 +8,10 @@ import ( "io" "os" "sort" + "sync" ) -// A Termination says why Getopt returned. +// A State is why the Getopt returned. type State int const ( @@ -25,7 +26,8 @@ const ( ) type Set struct { - State // State of getopt + stateMu sync.Mutex + state State // args are the parameters remaining after parsing the optoins. args []string @@ -60,6 +62,20 @@ func New() *Set { return s } +func (s *Set) setState(state State) { + s.stateMu.Lock() + s.state = state + s.stateMu.Unlock() +} + +// State returns the current state of the Set s. The state is normally the +// reason the most recent call to Getopt returned. +func (s *Set) State() State { + s.stateMu.Lock() + defer s.stateMu.Unlock() + return s.state +} + // The default set of command-line options. var CommandLine = New() @@ -73,6 +89,9 @@ func Usage() { CommandLine.usage() } // found in os.Args. func Parse() { CommandLine.Parse(os.Args) } +// Same as parse but not found in version 1 of getopt. +func ParseV2() { CommandLine.Parse(os.Args) } + // Getops returns the result of calling Getop in the default option set with the // command line arguments found in os.Args. The fn function, which may be nil, // is passed to Getopt. @@ -128,18 +147,24 @@ func (s *Set) SetParameters(parameters string) { s.parameters = parameters } -// SetProgram sets the program name to program. Nomrally it is determined +// Parameters returns the parameters set by SetParameters on s. +func (s *Set) Parameters() string { return s.parameters } + +// SetProgram sets the program name to program. Normally it is determined // from the zeroth command line argument (see os.Args). func SetProgram(program string) { CommandLine.program = program } -// SetProgram sets s's program name to program. Nomrally it is determined +// SetProgram sets s's program name to program. Normally it is determined // from the zeroth argument passed to Getopt or Parse. func (s *Set) SetProgram(program string) { s.program = program } +// Program returns the program name associated with Set s. +func (s *Set) Program() string { return s.program } + // SetUsage sets the function used by Parse to display the commands usage // on error. It defaults to calling PrintUsage(os.Stderr). func SetUsage(usage func()) { diff --git a/signed.go b/signed.go index a0e059c..dc371a7 100644 --- a/signed.go +++ b/signed.go @@ -1,4 +1,4 @@ -// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2017 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -7,6 +7,7 @@ package getopt import ( "fmt" "strconv" + "sync" ) type signed int64 @@ -18,10 +19,15 @@ type SignedLimit struct { Max int64 // Maximum allowed value if both Min and Max are not 0 } -var signedLimits = make(map[*signed]*SignedLimit) +var ( + signedLimitsMu sync.Mutex + signedLimits = make(map[*signed]*SignedLimit) +) func (n *signed) Set(value string, opt Option) error { + signedLimitsMu.Lock() l := signedLimits[n] + signedLimitsMu.Unlock() if l == nil { return fmt.Errorf("no limits defined for %s", opt.Name()) } @@ -50,7 +56,9 @@ func (n *signed) Set(value string, opt Option) error { } func (n *signed) String() string { + signedLimitsMu.Lock() l := signedLimits[n] + signedLimitsMu.Unlock() if l != nil && l.Base != 0 { return strconv.FormatInt(int64(*n), l.Base) } @@ -62,36 +70,27 @@ func (n *signed) String() string { // at least one of the values are not 0. If Base is 0, the base is implied by // the string's prefix: base 16 for "0x", base 8 for "0", and base 10 otherwise. func Signed(name rune, value int64, l *SignedLimit, helpvalue ...string) *int64 { - return CommandLine.Signed(name, value, l, helpvalue...) + CommandLine.signedOption(&value, "", name, l, helpvalue...) + return &value } func (s *Set) Signed(name rune, value int64, l *SignedLimit, helpvalue ...string) *int64 { - return s.SignedLong("", name, value, l, helpvalue...) + s.signedOption(&value, "", name, l, helpvalue...) + return &value } func SignedLong(name string, short rune, value int64, l *SignedLimit, helpvalue ...string) *int64 { - return CommandLine.SignedLong(name, short, value, l, helpvalue...) + CommandLine.signedOption(&value, name, short, l, helpvalue...) + return &value } func (s *Set) SignedLong(name string, short rune, value int64, l *SignedLimit, helpvalue ...string) *int64 { - s.SignedVarLong(&value, name, short, l, helpvalue...) + s.signedOption(&value, name, short, l, helpvalue...) return &value } -func SignedVar(p *int64, name rune, l *SignedLimit, helpvalue ...string) Option { - return CommandLine.SignedVar(p, name, l, helpvalue...) -} - -func (s *Set) SignedVar(p *int64, name rune, l *SignedLimit, helpvalue ...string) Option { - return s.SignedVarLong(p, "", name, l, helpvalue...) -} - -func SignedVarLong(p *int64, name string, short rune, l *SignedLimit, helpvalue ...string) Option { - return CommandLine.SignedVarLong(p, name, short, l, helpvalue...) -} - -func (s *Set) SignedVarLong(p *int64, name string, short rune, l *SignedLimit, helpvalue ...string) Option { - opt := s.VarLong((*signed)(p), name, short, helpvalue...) +func (s *Set) signedOption(p *int64, name string, short rune, l *SignedLimit, helpvalue ...string) { + opt := s.FlagLong((*signed)(p), name, short, helpvalue...) if l.Base > 36 || l.Base == 1 || l.Base < 0 { fmt.Fprintf(stderr, "invalid base for %s: %d\n", opt.Name(), l.Base) exit(1) @@ -105,6 +104,7 @@ func (s *Set) SignedVarLong(p *int64, name string, short rune, l *SignedLimit, h exit(1) } lim := *l + signedLimitsMu.Lock() signedLimits[(*signed)(p)] = &lim - return opt + signedLimitsMu.Unlock() } diff --git a/signed_test.go b/signed_test.go index 2d0635a..768a06d 100644 --- a/signed_test.go +++ b/signed_test.go @@ -1,4 +1,4 @@ -// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2017 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/string.go b/string.go index 1121867..cab5733 100644 --- a/string.go +++ b/string.go @@ -1,53 +1,27 @@ -// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2017 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package getopt -type stringValue string - -func (s *stringValue) Set(value string, opt Option) error { - *s = stringValue(value) - return nil -} - -func (s *stringValue) String() string { - return string(*s) -} - // String returns a value option that stores is value as a string. The // initial value of the string is passed in value. func String(name rune, value string, helpvalue ...string) *string { - return CommandLine.String(name, value, helpvalue...) + CommandLine.Flag(&value, name, helpvalue...) + return &value } func (s *Set) String(name rune, value string, helpvalue ...string) *string { - p := value - s.StringVarLong(&p, "", name, helpvalue...) - return &p + s.Flag(&value, name, helpvalue...) + return &value } func StringLong(name string, short rune, value string, helpvalue ...string) *string { - return CommandLine.StringLong(name, short, value, helpvalue...) + CommandLine.FlagLong(&value, name, short, helpvalue...) + return &value } func (s *Set) StringLong(name string, short rune, value string, helpvalue ...string) *string { - s.StringVarLong(&value, name, short, helpvalue...) + s.FlagLong(&value, name, short, helpvalue...) return &value } - -func StringVar(p *string, name rune, helpvalue ...string) Option { - return CommandLine.StringVar(p, name, helpvalue...) -} - -func (s *Set) StringVar(p *string, name rune, helpvalue ...string) Option { - return s.VarLong((*stringValue)(p), "", name, helpvalue...) -} - -func StringVarLong(p *string, name string, short rune, helpvalue ...string) Option { - return CommandLine.StringVarLong(p, name, short, helpvalue...) -} - -func (s *Set) StringVarLong(p *string, name string, short rune, helpvalue ...string) Option { - return s.VarLong((*stringValue)(p), name, short, helpvalue...) -} diff --git a/string_test.go b/string_test.go index c39a56c..31abae8 100644 --- a/string_test.go +++ b/string_test.go @@ -1,4 +1,4 @@ -// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2017 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/uint.go b/uint.go deleted file mode 100644 index bdb82b4..0000000 --- a/uint.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2013 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -import ( - "fmt" - "strconv" -) - -type uintValue uint - -func (i *uintValue) Set(value string, opt Option) error { - v, err := strconv.ParseUint(value, 0, strconv.IntSize) - if err != nil { - if e, ok := err.(*strconv.NumError); ok { - switch e.Err { - case strconv.ErrRange: - err = fmt.Errorf("value out of range: %s", value) - case strconv.ErrSyntax: - err = fmt.Errorf("not a valid number: %s", value) - } - } - return err - } - *i = uintValue(v) - return nil -} - -func (i *uintValue) String() string { - return strconv.FormatUint(uint64(*i), 10) -} - -// Uint creates an option that parses its value as an unsigned integer. -func Uint(name rune, value uint, helpvalue ...string) *uint { - return CommandLine.Uint(name, value, helpvalue...) -} - -func (s *Set) Uint(name rune, value uint, helpvalue ...string) *uint { - return s.UintLong("", name, value, helpvalue...) -} - -func UintLong(name string, short rune, value uint, helpvalue ...string) *uint { - return CommandLine.UintLong(name, short, value, helpvalue...) -} - -func (s *Set) UintLong(name string, short rune, value uint, helpvalue ...string) *uint { - s.UintVarLong(&value, name, short, helpvalue...) - return &value -} - -func UintVar(p *uint, name rune, helpvalue ...string) Option { - return CommandLine.UintVar(p, name, helpvalue...) -} - -func (s *Set) UintVar(p *uint, name rune, helpvalue ...string) Option { - return s.UintVarLong(p, "", name, helpvalue...) -} - -func UintVarLong(p *uint, name string, short rune, helpvalue ...string) Option { - return CommandLine.UintVarLong(p, name, short, helpvalue...) -} - -func (s *Set) UintVarLong(p *uint, name string, short rune, helpvalue ...string) Option { - return s.VarLong((*uintValue)(p), name, short, helpvalue...) -} diff --git a/uint16.go b/uint16.go deleted file mode 100644 index 92013e0..0000000 --- a/uint16.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2013 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -import ( - "fmt" - "strconv" -) - -type uint16Value uint16 - -func (i *uint16Value) Set(value string, opt Option) error { - v, err := strconv.ParseUint(value, 0, 16) - if err != nil { - if e, ok := err.(*strconv.NumError); ok { - switch e.Err { - case strconv.ErrRange: - err = fmt.Errorf("value out of range: %s", value) - case strconv.ErrSyntax: - err = fmt.Errorf("not a valid number: %s", value) - } - } - return err - } - *i = uint16Value(v) - return nil -} - -func (i *uint16Value) String() string { - return strconv.FormatUint(uint64(*i), 10) -} - -// Uint16 creates an option that parses its value as an uint16. -func Uint16(name rune, value uint16, helpvalue ...string) *uint16 { - return CommandLine.Uint16(name, value, helpvalue...) -} - -func (s *Set) Uint16(name rune, value uint16, helpvalue ...string) *uint16 { - return s.Uint16Long("", name, value, helpvalue...) -} - -func Uint16Long(name string, short rune, value uint16, helpvalue ...string) *uint16 { - return CommandLine.Uint16Long(name, short, value, helpvalue...) -} - -func (s *Set) Uint16Long(name string, short rune, value uint16, helpvalue ...string) *uint16 { - s.Uint16VarLong(&value, name, short, helpvalue...) - return &value -} - -func Uint16Var(p *uint16, name rune, helpvalue ...string) Option { - return CommandLine.Uint16Var(p, name, helpvalue...) -} - -func (s *Set) Uint16Var(p *uint16, name rune, helpvalue ...string) Option { - return s.Uint16VarLong(p, "", name, helpvalue...) -} - -func Uint16VarLong(p *uint16, name string, short rune, helpvalue ...string) Option { - return CommandLine.Uint16VarLong(p, name, short, helpvalue...) -} - -func (s *Set) Uint16VarLong(p *uint16, name string, short rune, helpvalue ...string) Option { - return s.VarLong((*uint16Value)(p), name, short, helpvalue...) -} diff --git a/uint16_test.go b/uint16_test.go deleted file mode 100644 index 5363a0c..0000000 --- a/uint16_test.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2013 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -import ( - "fmt" - "strings" - "testing" -) - -var uint16Tests = []struct { - where string - in []string - i uint16 - uint16 uint16 - err string -}{ - { - loc(), - []string{}, - 17, 42, - "", - }, - { - loc(), - []string{"test", "-i", "1", "--uint16", "2"}, - 1, 2, - "", - }, - { - loc(), - []string{"test", "-i1", "--uint16=2"}, - 1, 2, - "", - }, - { - loc(), - []string{"test", "-i1", "-i2"}, - 2, 42, - "", - }, - { - loc(), - []string{"test", "-i=1"}, - 17, 42, - "test: not a valid number: =1\n", - }, - { - loc(), - []string{"test", "-i0x20"}, - 0x20, 42, - "", - }, - { - loc(), - []string{"test", "-i010"}, - 8, 42, - "", - }, -} - -func TestUint16(t *testing.T) { - for x, tt := range uint16Tests { - reset() - i := Uint16('i', 17) - opt := Uint16Long("uint16", 0, 42) - if strings.Index(tt.where, ":-") > 0 { - tt.where = fmt.Sprintf("#%d", x) - } - - parse(tt.in) - if s := checkError(tt.err); s != "" { - t.Errorf("%s: %s", tt.where, s) - } - if got, want := *i, tt.i; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - if got, want := *opt, tt.uint16; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - } -} diff --git a/uint32.go b/uint32.go deleted file mode 100644 index abe911d..0000000 --- a/uint32.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2013 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -import ( - "fmt" - "strconv" -) - -type uint32Value uint32 - -func (i *uint32Value) Set(value string, opt Option) error { - v, err := strconv.ParseUint(value, 0, 32) - if err != nil { - if e, ok := err.(*strconv.NumError); ok { - switch e.Err { - case strconv.ErrRange: - err = fmt.Errorf("value out of range: %s", value) - case strconv.ErrSyntax: - err = fmt.Errorf("not a valid number: %s", value) - } - } - return err - } - *i = uint32Value(v) - return nil -} - -func (i *uint32Value) String() string { - return strconv.FormatUint(uint64(*i), 10) -} - -// Uint32 creates an option that parses its value as an uint32. -func Uint32(name rune, value uint32, helpvalue ...string) *uint32 { - return CommandLine.Uint32(name, value, helpvalue...) -} - -func (s *Set) Uint32(name rune, value uint32, helpvalue ...string) *uint32 { - return s.Uint32Long("", name, value, helpvalue...) -} - -func Uint32Long(name string, short rune, value uint32, helpvalue ...string) *uint32 { - return CommandLine.Uint32Long(name, short, value, helpvalue...) -} - -func (s *Set) Uint32Long(name string, short rune, value uint32, helpvalue ...string) *uint32 { - s.Uint32VarLong(&value, name, short, helpvalue...) - return &value -} - -func Uint32Var(p *uint32, name rune, helpvalue ...string) Option { - return CommandLine.Uint32Var(p, name, helpvalue...) -} - -func (s *Set) Uint32Var(p *uint32, name rune, helpvalue ...string) Option { - return s.Uint32VarLong(p, "", name, helpvalue...) -} - -func Uint32VarLong(p *uint32, name string, short rune, helpvalue ...string) Option { - return CommandLine.Uint32VarLong(p, name, short, helpvalue...) -} - -func (s *Set) Uint32VarLong(p *uint32, name string, short rune, helpvalue ...string) Option { - return s.VarLong((*uint32Value)(p), name, short, helpvalue...) -} diff --git a/uint32_test.go b/uint32_test.go deleted file mode 100644 index 80b4d11..0000000 --- a/uint32_test.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2013 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -import ( - "fmt" - "strings" - "testing" -) - -var uint32Tests = []struct { - where string - in []string - i uint32 - uint32 uint32 - err string -}{ - { - loc(), - []string{}, - 17, 42, - "", - }, - { - loc(), - []string{"test", "-i", "1", "--uint32", "2"}, - 1, 2, - "", - }, - { - loc(), - []string{"test", "-i1", "--uint32=2"}, - 1, 2, - "", - }, - { - loc(), - []string{"test", "-i1", "-i2"}, - 2, 42, - "", - }, - { - loc(), - []string{"test", "-i=1"}, - 17, 42, - "test: not a valid number: =1\n", - }, - { - loc(), - []string{"test", "-i0x20"}, - 0x20, 42, - "", - }, - { - loc(), - []string{"test", "-i010"}, - 8, 42, - "", - }, -} - -func TestUint32(t *testing.T) { - for x, tt := range uint32Tests { - reset() - i := Uint32('i', 17) - opt := Uint32Long("uint32", 0, 42) - if strings.Index(tt.where, ":-") > 0 { - tt.where = fmt.Sprintf("#%d", x) - } - - parse(tt.in) - if s := checkError(tt.err); s != "" { - t.Errorf("%s: %s", tt.where, s) - } - if got, want := *i, tt.i; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - if got, want := *opt, tt.uint32; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - } -} diff --git a/uint64.go b/uint64.go deleted file mode 100644 index 788e97f..0000000 --- a/uint64.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2013 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -import ( - "fmt" - "strconv" -) - -type uint64Value uint64 - -func (i *uint64Value) Set(value string, opt Option) error { - v, err := strconv.ParseUint(value, 0, 64) - if err != nil { - if e, ok := err.(*strconv.NumError); ok { - switch e.Err { - case strconv.ErrRange: - err = fmt.Errorf("value out of range: %s", value) - case strconv.ErrSyntax: - err = fmt.Errorf("not a valid number: %s", value) - } - } - return err - } - *i = uint64Value(v) - return nil -} - -func (i *uint64Value) String() string { - return strconv.FormatUint(uint64(*i), 10) -} - -// Uint64 creates an option that parses its value as a uint64. -func Uint64(name rune, value uint64, helpvalue ...string) *uint64 { - return CommandLine.Uint64(name, value, helpvalue...) -} - -func (s *Set) Uint64(name rune, value uint64, helpvalue ...string) *uint64 { - return s.Uint64Long("", name, value, helpvalue...) -} - -func Uint64Long(name string, short rune, value uint64, helpvalue ...string) *uint64 { - return CommandLine.Uint64Long(name, short, value, helpvalue...) -} - -func (s *Set) Uint64Long(name string, short rune, value uint64, helpvalue ...string) *uint64 { - s.Uint64VarLong(&value, name, short, helpvalue...) - return &value -} - -func Uint64Var(p *uint64, name rune, helpvalue ...string) Option { - return CommandLine.Uint64Var(p, name, helpvalue...) -} - -func (s *Set) Uint64Var(p *uint64, name rune, helpvalue ...string) Option { - return s.Uint64VarLong(p, "", name, helpvalue...) -} - -func Uint64VarLong(p *uint64, name string, short rune, helpvalue ...string) Option { - return CommandLine.Uint64VarLong(p, name, short, helpvalue...) -} - -func (s *Set) Uint64VarLong(p *uint64, name string, short rune, helpvalue ...string) Option { - return s.VarLong((*uint64Value)(p), name, short, helpvalue...) -} diff --git a/uint64_test.go b/uint64_test.go deleted file mode 100644 index 962d369..0000000 --- a/uint64_test.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2013 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -import ( - "fmt" - "strings" - "testing" -) - -var uint64Tests = []struct { - where string - in []string - i uint64 - uint64 uint64 - err string -}{ - { - loc(), - []string{}, - 17, 42, - "", - }, - { - loc(), - []string{"test", "-i", "1", "--uint64", "2"}, - 1, 2, - "", - }, - { - loc(), - []string{"test", "-i1", "--uint64=2"}, - 1, 2, - "", - }, - { - loc(), - []string{"test", "-i1", "-i2"}, - 2, 42, - "", - }, - { - loc(), - []string{"test", "-i=1"}, - 17, 42, - "test: not a valid number: =1\n", - }, - { - loc(), - []string{"test", "-i0x20"}, - 0x20, 42, - "", - }, - { - loc(), - []string{"test", "-i010"}, - 8, 42, - "", - }, -} - -func TestUint64(t *testing.T) { - for x, tt := range uint64Tests { - reset() - i := Uint64('i', 17) - opt := Uint64Long("uint64", 0, 42) - if strings.Index(tt.where, ":-") > 0 { - tt.where = fmt.Sprintf("#%d", x) - } - - parse(tt.in) - if s := checkError(tt.err); s != "" { - t.Errorf("%s: %s", tt.where, s) - } - if got, want := *i, tt.i; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - if got, want := *opt, tt.uint64; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - } -} diff --git a/uint_test.go b/uint_test.go deleted file mode 100644 index 48c38cb..0000000 --- a/uint_test.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2013 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package getopt - -import ( - "fmt" - "strings" - "testing" -) - -var uintTests = []struct { - where string - in []string - i uint - uint uint - err string -}{ - { - loc(), - []string{}, - 17, 42, - "", - }, - { - loc(), - []string{"test", "-i", "1", "--uint", "2"}, - 1, 2, - "", - }, - { - loc(), - []string{"test", "-i1", "--uint=2"}, - 1, 2, - "", - }, - { - loc(), - []string{"test", "-i1", "-i2"}, - 2, 42, - "", - }, - { - loc(), - []string{"test", "-i=1"}, - 17, 42, - "test: not a valid number: =1\n", - }, - { - loc(), - []string{"test", "-i0x20"}, - 0x20, 42, - "", - }, - { - loc(), - []string{"test", "-i010"}, - 8, 42, - "", - }, -} - -func TestUint(t *testing.T) { - for x, tt := range uintTests { - reset() - i := Uint('i', 17) - opt := UintLong("uint", 0, 42) - if strings.Index(tt.where, ":-") > 0 { - tt.where = fmt.Sprintf("#%d", x) - } - - parse(tt.in) - if s := checkError(tt.err); s != "" { - t.Errorf("%s: %s", tt.where, s) - } - if got, want := *i, tt.i; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - if got, want := *opt, tt.uint; got != want { - t.Errorf("%s: got %v, want %v", tt.where, got, want) - } - } -} diff --git a/unsigned.go b/unsigned.go index 3ded0e7..5594660 100644 --- a/unsigned.go +++ b/unsigned.go @@ -1,4 +1,4 @@ -// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2017 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -7,6 +7,7 @@ package getopt import ( "fmt" "strconv" + "sync" ) type unsigned uint64 @@ -18,10 +19,15 @@ type UnsignedLimit struct { Max uint64 // Maximum allowed value if both Min and Max are not 0 } -var unsignedLimits = make(map[*unsigned]*UnsignedLimit) +var ( + unsignedLimitsMu sync.Mutex + unsignedLimits = make(map[*unsigned]*UnsignedLimit) +) func (n *unsigned) Set(value string, opt Option) error { + unsignedLimitsMu.Lock() l := unsignedLimits[n] + unsignedLimitsMu.Unlock() if l == nil { return fmt.Errorf("no limits defined for %s", opt.Name()) } @@ -50,7 +56,9 @@ func (n *unsigned) Set(value string, opt Option) error { } func (n *unsigned) String() string { + unsignedLimitsMu.Lock() l := unsignedLimits[n] + unsignedLimitsMu.Unlock() if l != nil && l.Base != 0 { return strconv.FormatUint(uint64(*n), l.Base) } @@ -63,36 +71,27 @@ func (n *unsigned) String() string { // implied by the string's prefix: base 16 for "0x", base 8 for "0", and base // 10 otherwise. func Unsigned(name rune, value uint64, l *UnsignedLimit, helpvalue ...string) *uint64 { - return CommandLine.Unsigned(name, value, l, helpvalue...) + CommandLine.unsignedOption(&value, "", name, l, helpvalue...) + return &value } func (s *Set) Unsigned(name rune, value uint64, l *UnsignedLimit, helpvalue ...string) *uint64 { - return s.UnsignedLong("", name, value, l, helpvalue...) + s.unsignedOption(&value, "", name, l, helpvalue...) + return &value } func UnsignedLong(name string, short rune, value uint64, l *UnsignedLimit, helpvalue ...string) *uint64 { - return CommandLine.UnsignedLong(name, short, value, l, helpvalue...) + CommandLine.unsignedOption(&value, name, short, l, helpvalue...) + return &value } func (s *Set) UnsignedLong(name string, short rune, value uint64, l *UnsignedLimit, helpvalue ...string) *uint64 { - s.UnsignedVarLong(&value, name, short, l, helpvalue...) + s.unsignedOption(&value, name, short, l, helpvalue...) return &value } -func UnsignedVar(p *uint64, name rune, l *UnsignedLimit, helpvalue ...string) Option { - return CommandLine.UnsignedVar(p, name, l, helpvalue...) -} - -func (s *Set) UnsignedVar(p *uint64, name rune, l *UnsignedLimit, helpvalue ...string) Option { - return s.UnsignedVarLong(p, "", name, l, helpvalue...) -} - -func UnsignedVarLong(p *uint64, name string, short rune, l *UnsignedLimit, helpvalue ...string) Option { - return CommandLine.UnsignedVarLong(p, name, short, l, helpvalue...) -} - -func (s *Set) UnsignedVarLong(p *uint64, name string, short rune, l *UnsignedLimit, helpvalue ...string) Option { - opt := s.VarLong((*unsigned)(p), name, short, helpvalue...) +func (s *Set) unsignedOption(p *uint64, name string, short rune, l *UnsignedLimit, helpvalue ...string) { + opt := s.FlagLong((*unsigned)(p), name, short, helpvalue...) if l.Base > 36 || l.Base == 1 || l.Base < 0 { fmt.Fprintf(stderr, "invalid base for %s: %d\n", opt.Name(), l.Base) exit(1) @@ -106,6 +105,7 @@ func (s *Set) UnsignedVarLong(p *uint64, name string, short rune, l *UnsignedLim exit(1) } lim := *l + unsignedLimitsMu.Lock() unsignedLimits[(*unsigned)(p)] = &lim - return opt + unsignedLimitsMu.Unlock() } diff --git a/unsigned_test.go b/unsigned_test.go index 0fd60d4..bf3d61a 100644 --- a/unsigned_test.go +++ b/unsigned_test.go @@ -1,4 +1,4 @@ -// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2017 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. diff --git a/util_test.go b/util_test.go index 79a732a..d46e198 100644 --- a/util_test.go +++ b/util_test.go @@ -1,4 +1,4 @@ -// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2017 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -70,8 +70,6 @@ func (o *option) Equal(opt *option) bool { return reflect.DeepEqual(&oc, &optc) } -func newStringValue(s string) *stringValue { return (*stringValue)(&s) } - func checkError(err string) string { switch { case err == errorString: diff --git a/var.go b/var.go index fd816b9..c60a9d2 100644 --- a/var.go +++ b/var.go @@ -1,4 +1,4 @@ -// Copyright 2013 Google Inc. All rights reserved. +// Copyright 2017 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -7,33 +7,70 @@ package getopt import ( "fmt" "runtime" + "strings" ) -// Value is the interface to the dynamic value stored in a flag. (The default -// value is represented as a string.) Set is passed the string to set the -// value to as well as the Option that is being processed. +// Value is the interface to the dynamic value stored in a flag. Flags of type +// Value are declared using the Flag and FlagLong functions. type Value interface { - Set(string, Option) error + // Set converts value into the appropriate type and assigns it to the + // receiver value. Option details are provided via opt (such as the + // flags name). + // + // Set is used to reset the value of an option to its default value + // (which is stored in string form internally). + Set(value string, opt Option) error + + // String returns the value of the flag as a string. String() string } -// Var creates an option of the specified name. The type and value of the option -// are represented by the first argument, of type Value, which typically holds a -// user-defined implementation of Value. All options are ultimately created -// as a Var. -func Var(p Value, name rune, helpvalue ...string) Option { - return CommandLine.VarLong(p, "", name, helpvalue...) -} +var thisPackage string -func VarLong(p Value, name string, short rune, helpvalue ...string) Option { - return CommandLine.VarLong(p, name, short, helpvalue...) +// init initializes thisPackage to our full package with the trailing . +// included. +func init() { + pc, _, _, ok := runtime.Caller(0) + if !ok { + return + } + f := runtime.FuncForPC(pc) + if f == nil { + return + } + thisPackage = f.Name() + x := strings.LastIndex(thisPackage, "/") + if x < 0 { + return + } + y := strings.Index(thisPackage[x:], ".") + if y < 0 { + return + } + // thisPackage includes the trailing . after the package name. + thisPackage = thisPackage[:x+y+1] } -func (s *Set) Var(p Value, name rune, helpvalue ...string) Option { - return s.VarLong(p, "", name, helpvalue...) +// calledFrom returns a string containing the file and linenumber of the first +// stack frame above us that is not part of this package and is not a test. +// This is used to determine where a flag was initialized. +func calledFrom() string { + for i := 2; ; i++ { + pc, file, line, ok := runtime.Caller(i) + if !ok { + return "" + } + if !strings.HasSuffix(file, "_test.go") { + f := runtime.FuncForPC(pc) + if f != nil && strings.HasPrefix(f.Name(), thisPackage) { + continue + } + } + return fmt.Sprintf("%s:%d", file, line) + } } -func (s *Set) VarLong(p Value, name string, short rune, helpvalue ...string) Option { +func (s *Set) addFlag(p Value, name string, short rune, helpvalue ...string) Option { opt := &option{ short: short, long: name, @@ -51,8 +88,8 @@ func (s *Set) VarLong(p Value, name string, short rune, helpvalue ...string) Opt default: panic("Too many strings for String helpvalue") } - if _, file, line, ok := runtime.Caller(1); ok { - opt.where = fmt.Sprintf("%s:%d", file, line) + if where := calledFrom(); where != "" { + opt.where = where } if opt.short == 0 && opt.long == "" { fmt.Fprintf(stderr, opt.where+": no short or long option given") From 50677548864d8872101edbf5d1d84cbf546467e3 Mon Sep 17 00:00:00 2001 From: Tim Otlik Date: Fri, 28 Jun 2019 23:57:44 +0200 Subject: [PATCH 3/4] chore: add v2 to module path --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 66c9762..59e1aee 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ -module github.com/sambadevi/getopt +module github.com/sambadevi/getopt/v2 go 1.12 From b60e4a53bc20d0dd6cab6e950f53e0ac6ec53aef Mon Sep 17 00:00:00 2001 From: Tim Otlik Date: Sat, 29 Jun 2019 00:06:04 +0200 Subject: [PATCH 4/4] chore: update go.mod path to path of original author note: this commit should be tagged as version 1 (v1.x.x) --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 66c9762..4c5d767 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ -module github.com/sambadevi/getopt +module github.com/pborman/getopt go 1.12