diff --git a/go.mod b/go.mod index 311db62..5511745 100644 --- a/go.mod +++ b/go.mod @@ -4,5 +4,5 @@ go 1.13 require ( github.com/mattn/go-xmpp v0.0.0-20200309091041-899ef71e80d2 - github.com/pborman/getopt v0.0.0-20190409184431-ee0cd42419d3 + github.com/pborman/getopt/v2 v2.1.0 ) diff --git a/go.sum b/go.sum index 90c2a00..91ceb36 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,4 @@ github.com/mattn/go-xmpp v0.0.0-20200309091041-899ef71e80d2 h1:F544zRtDc/pMpFNHN46oeXV2jIAG4DoMH+6zlVSn0Q8= github.com/mattn/go-xmpp v0.0.0-20200309091041-899ef71e80d2/go.mod h1:Cs5mF0OsrRRmhkyOod//ldNPOwJsrBvJ+1WRspv0xoc= -github.com/pborman/getopt v0.0.0-20190409184431-ee0cd42419d3 h1:YtFkrqsMEj7YqpIhRteVxJxCeC3jJBieuLr0d4C4rSA= -github.com/pborman/getopt v0.0.0-20190409184431-ee0cd42419d3/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= +github.com/pborman/getopt/v2 v2.1.0 h1:eNfR+r+dWLdWmV8g5OlpyrTYHkhVNxHBdN2cCrJmOEA= +github.com/pborman/getopt/v2 v2.1.0/go.mod h1:4NtW75ny4eBw9fO1bhtNdYTlZKYX5/tBLtsOpwKIKd0= diff --git a/vendor/github.com/pborman/getopt/AUTHORS b/vendor/github.com/pborman/getopt/AUTHORS deleted file mode 100644 index 2d70b04..0000000 --- a/vendor/github.com/pborman/getopt/AUTHORS +++ /dev/null @@ -1 +0,0 @@ -Paul Borman diff --git a/vendor/github.com/pborman/getopt/LICENSE b/vendor/github.com/pborman/getopt/v2/LICENSE similarity index 100% rename from vendor/github.com/pborman/getopt/LICENSE rename to vendor/github.com/pborman/getopt/v2/LICENSE diff --git a/vendor/github.com/pborman/getopt/v2/getopt.go b/vendor/github.com/pborman/getopt/v2/getopt.go index 1ffbfaa..e5c52bf 100644 --- a/vendor/github.com/pborman/getopt/v2/getopt.go +++ b/vendor/github.com/pborman/getopt/v2/getopt.go @@ -152,6 +152,34 @@ // 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. // +// MANDATORY OPTIONS +// +// An option marked as mandatory and not seen when parsing will cause an error +// to be reported such as: "program: --name is a mandatory option". An option +// is marked mandatory by using the Mandatory method: +// +// getopt.FlagLong(&fileName, "path", 0, "the path").Mandatory() +// +// Mandatory options have (required) appended to their help message: +// +// --path=value the path (required) +// +// MUTUALLY EXCLUSIVE OPTIONS +// +// Options can be marked as part of a mutually exclusive group. When two or +// more options in a mutually exclusive group are both seen while parsing then +// an error such as "program: options -a and -b are mutually exclusive" will be +// reported. Mutually exclusive groups are declared using the SetGroup method: +// +// getopt.Flag(&a, 'a', "use method A").SetGroup("method") +// getopt.Flag(&a, 'b', "use method B").SetGroup("method") +// +// A set can have multiple mutually exclusive groups. Mutually exclusive groups +// are identified with their group name in {}'s appeneded to their help message: +// +// -a use method A {method} +// -b use method B {method} +// // BUILTIN TYPES // // The Flag and FlagLong functions support most standard Go types. For the @@ -318,7 +346,7 @@ func (s *Set) PrintOptions(w io.Writer) { for _, opt := range s.options { if opt.uname != "" { opt.help = strings.TrimSpace(opt.help) - if len(opt.help) == 0 { + if len(opt.help) == 0 && !opt.mandatory && opt.group == "" { fmt.Fprintf(w, " %s\n", opt.uname) continue } @@ -350,6 +378,12 @@ func (s *Set) PrintOptions(w io.Writer) { if def != "" { helpMsg += " [" + def + "]" } + if opt.group != "" { + helpMsg += " {" + opt.group + "}" + } + if opt.mandatory { + helpMsg += " (required)" + } help := strings.Split(helpMsg, "\n") // If they did not put in newlines then we will insert @@ -444,6 +478,12 @@ func (s *Set) Getopt(args []string, fn func(Option) bool) (err error) { } } }() + + defer func() { + if err == nil { + err = s.checkOptions() + } + }() if fn == nil { fn = func(Option) bool { return true } } @@ -562,3 +602,35 @@ Parsing: s.args = []string{} return nil } + +func (s *Set) checkOptions() error { + groups := map[string]Option{} + for _, opt := range s.options { + if !opt.Seen() { + if opt.mandatory { + return fmt.Errorf("option %s is mandatory", opt.Name()) + } + continue + } + if opt.group == "" { + continue + } + if opt2 := groups[opt.group]; opt2 != nil { + return fmt.Errorf("options %s and %s are mutually exclusive", opt2.Name(), opt.Name()) + } + groups[opt.group] = opt + } + for _, group := range s.requiredGroups { + if groups[group] != nil { + continue + } + var flags []string + for _, opt := range s.options { + if opt.group == group { + flags = append(flags, opt.Name()) + } + } + return fmt.Errorf("exactly one of the following options must be specified: %s", strings.Join(flags, ", ")) + } + return nil +} diff --git a/vendor/github.com/pborman/getopt/v2/go.mod b/vendor/github.com/pborman/getopt/v2/go.mod new file mode 100644 index 0000000..17af40c --- /dev/null +++ b/vendor/github.com/pborman/getopt/v2/go.mod @@ -0,0 +1,3 @@ +module github.com/pborman/getopt/v2 + +go 1.13 diff --git a/vendor/github.com/pborman/getopt/v2/option.go b/vendor/github.com/pborman/getopt/v2/option.go index 67822bc..c97bb29 100644 --- a/vendor/github.com/pborman/getopt/v2/option.go +++ b/vendor/github.com/pborman/getopt/v2/option.go @@ -59,21 +59,31 @@ type Option interface { // yet been seen, including resetting the value of the option // to its original default state. Reset() + + // Mandataory sets the mandatory flag of the option. Parse will + // fail if a mandatory option is missing. + Mandatory() Option + + // SetGroup sets the option as part of a radio group. Parse will + // fail if two options in the same group are seen. + SetGroup(string) Option } 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) + 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) + mandatory bool // this option must be specified + group string // mutual exclusion group } // usageName returns the name of the option for printing usage lines in one @@ -121,12 +131,14 @@ func (o *option) sortName() string { 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) 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) Mandatory() Option { o.mandatory = true; return o } +func (o *option) SetGroup(g string) Option { o.group = g; return o } func (o *option) Value() Value { if o == nil { diff --git a/vendor/github.com/pborman/getopt/v2/set.go b/vendor/github.com/pborman/getopt/v2/set.go index a7895c8..9f73ede 100644 --- a/vendor/github.com/pborman/getopt/v2/set.go +++ b/vendor/github.com/pborman/getopt/v2/set.go @@ -46,6 +46,7 @@ type Set struct { shortOptions map[rune]*option longOptions map[string]*option options optionList + requiredGroups []string } // New returns a newly created option set. @@ -291,3 +292,18 @@ func (s *Set) Reset() { opt.Reset() } } + +// RequiredGroup marks the group set with Option.SetGroup as required. At least +// one option in the group must be seen by parse. Calling RequiredGroup with a +// group name that has no options will cause parsing to always fail. +func (s *Set) RequiredGroup(group string) { + s.requiredGroups = append(s.requiredGroups, group) +} + +// RequiredGroup marks the group set with Option.SetGroup as required on the +// command line. At least one option in the group must be seen by parse. +// Calling RequiredGroup with a group name that has no options will cause +// parsing to always fail. +func RequiredGroup(group string) { + CommandLine.requiredGroups = append(CommandLine.requiredGroups, group) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 48fe0b8..f6fdf1c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,4 +1,4 @@ # github.com/mattn/go-xmpp v0.0.0-20200309091041-899ef71e80d2 github.com/mattn/go-xmpp -# github.com/pborman/getopt v0.0.0-20190409184431-ee0cd42419d3 +# github.com/pborman/getopt/v2 v2.1.0 github.com/pborman/getopt/v2