switch flags, revendor

This commit is contained in:
Umputun
2020-04-13 12:30:23 -05:00
parent d6c7e151a0
commit d7442ef1a9
41 changed files with 241 additions and 87 deletions
@@ -6,8 +6,6 @@ os:
go:
- 1.x
- 1.7.x
- 1.8.x
- 1.9.x
- 1.10.x
@@ -17,7 +15,7 @@ install:
- go build -v ./...
# linting
- go get github.com/golang/lint/golint
- go get -v golang.org/x/lint/golint
# code coverage
- go get golang.org/x/tools/cmd/cover
@@ -61,6 +61,9 @@ var opts struct {
// Example of a required flag
Name string `short:"n" long:"name" description:"A name" required:"true"`
// Example of a flag restricted to a pre-defined set of strings
Animal string `long:"animal" choice:"cat" choice:"dog"`
// Example of a value name
File string `short:"f" long:"file" description:"A file" value-name:"FILE"`
@@ -91,6 +94,7 @@ args := []string{
"-vv",
"--offset=5",
"-n", "Me",
"--animal", "dog", // anything other than "cat" or "dog" will raise an error
"-p", "3",
"-s", "hello",
"-s", "world",
@@ -115,6 +119,7 @@ if err != nil {
fmt.Printf("Verbosity: %v\n", opts.Verbose)
fmt.Printf("Offset: %d\n", opts.Offset)
fmt.Printf("Name: %s\n", opts.Name)
fmt.Printf("Animal: %s\n", opts.Animal)
fmt.Printf("Ptr: %d\n", *opts.Ptr)
fmt.Printf("StringSlice: %v\n", opts.StringSlice)
fmt.Printf("PtrSlice: [%v %v]\n", *opts.PtrSlice[0], *opts.PtrSlice[1])
@@ -438,7 +438,7 @@ func (c *Command) match(name string) bool {
return false
}
func (c *Command) hasCliOptions() bool {
func (c *Command) hasHelpOptions() bool {
ret := false
c.eachGroup(func(g *Group) {
@@ -447,7 +447,7 @@ func (c *Command) hasCliOptions() bool {
}
for _, opt := range g.options {
if opt.canCli() {
if opt.showInHelp() {
ret = true
}
}
@@ -2,6 +2,7 @@ package flags
import (
"fmt"
"os"
"path/filepath"
"reflect"
"sort"
@@ -62,6 +63,11 @@ func completionsWithoutDescriptions(items []string) []Completion {
// prefix.
func (f *Filename) Complete(match string) []Completion {
ret, _ := filepath.Glob(match + "*")
if len(ret) == 1 {
if info, err := os.Stat(ret[0]); err == nil && info.IsDir() {
ret[0] = ret[0] + "/"
}
}
return completionsWithoutDescriptions(ret)
}
@@ -28,6 +28,15 @@ type Unmarshaler interface {
UnmarshalFlag(value string) error
}
// ValueValidator is the interface implemented by types that can validate a
// flag argument themselves. The provided value is directly passed from the
// command line.
type ValueValidator interface {
// IsValidValue returns an error if the provided string value is valid for
// the flag.
IsValidValue(value string) error
}
func getBase(options multiTag, base int) (int, error) {
sbase := options.Get("base")
@@ -109,7 +109,8 @@ The following is a list of tags for struct fields supported by go-flags:
value-name: the name of the argument value (to be shown in the help)
(optional)
choice: limits the values for an option to a set of values.
This tag can be specified multiple times (optional)
Repeat this tag once for each allowable value.
e.g. `long:"animal" choice:"cat" choice:"dog"`
hidden: if non-empty, the option is not visible in the help or man page.
base: a base (radix) used to convert strings to integer values, the
+3
View File
@@ -0,0 +1,3 @@
module github.com/umputun/go-flags
go 1.12
@@ -168,6 +168,18 @@ func (g *Group) optionByName(name string, namematch func(*Option, string) bool)
return retopt
}
func (g *Group) showInHelp() bool {
if g.Hidden {
return false
}
for _, opt := range g.options {
if opt.showInHelp() {
return true
}
}
return false
}
func (g *Group) eachGroup(f func(*Group)) {
f(g)
@@ -72,6 +72,9 @@ func (p *Parser) getAlignmentInfo() alignmentInfo {
var prevcmd *Command
p.eachActiveGroup(func(c *Command, grp *Group) {
if !grp.showInHelp() {
return
}
if c != prevcmd {
for _, arg := range c.args {
ret.updateLen(arg.Name, c != p.Command)
@@ -79,7 +82,7 @@ func (p *Parser) getAlignmentInfo() alignmentInfo {
}
for _, info := range grp.options {
if !info.canCli() {
if !info.showInHelp() {
continue
}
@@ -305,7 +308,7 @@ func (p *Parser) WriteHelp(writer io.Writer) {
}
} else if us, ok := allcmd.data.(Usage); ok {
usage = us.Usage()
} else if allcmd.hasCliOptions() {
} else if allcmd.hasHelpOptions() {
usage = fmt.Sprintf("[%s-OPTIONS]", allcmd.Name)
}
@@ -393,7 +396,7 @@ func (p *Parser) WriteHelp(writer io.Writer) {
}
for _, info := range grp.options {
if !info.canCli() || info.Hidden {
if !info.showInHelp() {
continue
}
@@ -489,3 +492,23 @@ func (p *Parser) WriteHelp(writer io.Writer) {
wr.Flush()
}
// WroteHelp is a helper to test the error from ParseArgs() to
// determine if the help message was written. It is safe to
// call without first checking that error is nil.
func WroteHelp(err error) bool {
if err == nil { // No error
return false
}
flagError, ok := err.(*Error)
if !ok { // Not a go-flag error
return false
}
if flagError.Type != ErrHelp { // Did not print the help message
return false
}
return true
}
@@ -3,7 +3,9 @@ package flags
import (
"fmt"
"io"
"os"
"runtime"
"strconv"
"strings"
"time"
)
@@ -38,7 +40,7 @@ func formatForMan(wr io.Writer, s string) {
func writeManPageOptions(wr io.Writer, grp *Group) {
grp.eachGroup(func(group *Group) {
if group.Hidden || len(group.options) == 0 {
if !group.showInHelp() {
return
}
@@ -54,7 +56,7 @@ func writeManPageOptions(wr io.Writer, grp *Group) {
}
for _, opt := range group.options {
if !opt.canCli() || opt.Hidden {
if !opt.showInHelp() {
continue
}
@@ -148,12 +150,12 @@ func writeManPageCommand(wr io.Writer, name string, root *Command, command *Comm
var usage string
if us, ok := command.data.(Usage); ok {
usage = us.Usage()
} else if command.hasCliOptions() {
} else if command.hasHelpOptions() {
usage = fmt.Sprintf("[%s-OPTIONS]", command.Name)
}
var pre string
if root.hasCliOptions() {
if root.hasHelpOptions() {
pre = fmt.Sprintf("%s [OPTIONS] %s", root.Name, command.Name)
} else {
pre = fmt.Sprintf("%s %s", root.Name, command.Name)
@@ -175,6 +177,14 @@ func writeManPageCommand(wr io.Writer, name string, root *Command, command *Comm
// writer.
func (p *Parser) WriteManPage(wr io.Writer) {
t := time.Now()
source_date_epoch := os.Getenv("SOURCE_DATE_EPOCH")
if source_date_epoch != "" {
sde, err := strconv.ParseInt(source_date_epoch, 10, 64)
if err != nil {
panic(fmt.Sprintf("Invalid SOURCE_DATE_EPOCH: %s", err))
}
t = time.Unix(sde, 0)
}
fmt.Fprintf(wr, ".TH %s 1 \"%s\"\n", manQuote(p.Name), t.Format("2 January 2006"))
fmt.Fprintln(wr, ".SH NAME")
@@ -280,8 +280,8 @@ func (option *Option) set(value *string) error {
return convert("", option.value, option.tag)
}
func (option *Option) canCli() bool {
return option.ShortName != 0 || len(option.LongName) != 0
func (option *Option) showInHelp() bool {
return !option.Hidden && (option.ShortName != 0 || len(option.LongName) != 0)
}
func (option *Option) canArgument() bool {
@@ -389,6 +389,30 @@ func (option *Option) isUnmarshaler() Unmarshaler {
return nil
}
func (option *Option) isValueValidator() ValueValidator {
v := option.value
for {
if !v.CanInterface() {
break
}
i := v.Interface()
if u, ok := i.(ValueValidator); ok {
return u
}
if !v.CanAddr() {
break
}
v = v.Addr()
}
return nil
}
func (option *Option) isBool() bool {
tp := option.value.Type()
@@ -507,3 +531,13 @@ func (option *Option) shortAndLongName() string {
return ret.String()
}
func (option *Option) isValidValue(arg string) error {
if validator := option.isValueValidator(); validator != nil {
return validator.IsValidValue(arg)
}
if argumentIsOption(arg) && !(option.isSignedNumber() && len(arg) > 1 && arg[0] == '-' && arg[1] >= '0' && arg[1] <= '9') {
return fmt.Errorf("expected argument for flag `%s', but got option `%s'", option, arg)
}
return nil
}
@@ -241,6 +241,7 @@ func (p *Parser) ParseArgs(args []string) ([]string, error) {
p.fillParseState(s)
for !s.eof() {
var err error
arg := s.pop()
// When PassDoubleDash is set and we encounter a --, then
@@ -251,6 +252,20 @@ func (p *Parser) ParseArgs(args []string) ([]string, error) {
}
if !argumentIsOption(arg) {
if (p.Options&PassAfterNonOption) != None && s.lookup.commands[arg] == nil {
// If PassAfterNonOption is set then all remaining arguments
// are considered positional
if err = s.addArgs(s.arg); err != nil {
break
}
if err = s.addArgs(s.args...); err != nil {
break
}
break
}
// Note: this also sets s.err, so we can just check for
// nil here and use s.err later
if p.parseNonOption(s) != nil {
@@ -260,8 +275,6 @@ func (p *Parser) ParseArgs(args []string) ([]string, error) {
continue
}
var err error
prefix, optname, islong := stripOptionPrefix(arg)
optname, _, argument := splitOption(prefix, optname, islong)
@@ -519,8 +532,8 @@ func (p *Parser) parseOption(s *parseState, name string, option *Option, canarg
} else {
arg = s.pop()
if argumentIsOption(arg) && !(option.isSignedNumber() && len(arg) > 1 && arg[0] == '-' && arg[1] >= '0' && arg[1] <= '9') {
return newErrorf(ErrExpectedArgument, "expected argument for flag `%s', but got option `%s'", option, arg)
if validationErr := option.isValidValue(arg); validationErr != nil {
return newErrorf(ErrExpectedArgument, validationErr.Error())
} else if p.Options&PassDoubleDash != 0 && arg == "--" {
return newErrorf(ErrExpectedArgument, "expected argument for flag `%s', but got double dash `--'", option)
}
@@ -653,23 +666,7 @@ func (p *Parser) parseNonOption(s *parseState) error {
}
}
if (p.Options & PassAfterNonOption) != None {
// If PassAfterNonOption is set then all remaining arguments
// are considered positional
if err := s.addArgs(s.arg); err != nil {
return err
}
if err := s.addArgs(s.args...); err != nil {
return err
}
s.args = []string{}
} else {
return s.addArgs(s.arg)
}
return nil
return s.addArgs(s.arg)
}
func (p *Parser) showBuiltinHelp() error {
@@ -1,4 +1,4 @@
// +build !windows,!plan9,!solaris,!appengine
// +build !windows,!plan9,!solaris,!appengine,!wasm
package flags
@@ -1,4 +1,4 @@
// +build windows plan9 solaris appengine
// +build plan9 solaris appengine wasm
package flags
+85
View File
@@ -0,0 +1,85 @@
// +build windows
package flags
import (
"syscall"
"unsafe"
)
type (
SHORT int16
WORD uint16
SMALL_RECT struct {
Left SHORT
Top SHORT
Right SHORT
Bottom SHORT
}
COORD struct {
X SHORT
Y SHORT
}
CONSOLE_SCREEN_BUFFER_INFO struct {
Size COORD
CursorPosition COORD
Attributes WORD
Window SMALL_RECT
MaximumWindowSize COORD
}
)
var kernel32DLL = syscall.NewLazyDLL("kernel32.dll")
var getConsoleScreenBufferInfoProc = kernel32DLL.NewProc("GetConsoleScreenBufferInfo")
func getError(r1, r2 uintptr, lastErr error) error {
// If the function fails, the return value is zero.
if r1 == 0 {
if lastErr != nil {
return lastErr
}
return syscall.EINVAL
}
return nil
}
func getStdHandle(stdhandle int) (uintptr, error) {
handle, err := syscall.GetStdHandle(stdhandle)
if err != nil {
return 0, err
}
return uintptr(handle), nil
}
// GetConsoleScreenBufferInfo retrieves information about the specified console screen buffer.
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms683171(v=vs.85).aspx
func GetConsoleScreenBufferInfo(handle uintptr) (*CONSOLE_SCREEN_BUFFER_INFO, error) {
var info CONSOLE_SCREEN_BUFFER_INFO
if err := getError(getConsoleScreenBufferInfoProc.Call(handle, uintptr(unsafe.Pointer(&info)), 0)); err != nil {
return nil, err
}
return &info, nil
}
func getTerminalColumns() int {
defaultWidth := 80
stdoutHandle, err := getStdHandle(syscall.STD_OUTPUT_HANDLE)
if err != nil {
return defaultWidth
}
info, err := GetConsoleScreenBufferInfo(stdoutHandle)
if err != nil {
return defaultWidth
}
if info.MaximumWindowSize.X > 0 {
return int(info.MaximumWindowSize.X)
}
return defaultWidth
}