diff --git a/Gopkg.lock b/Gopkg.lock index 5acb6e3d..80ac9127 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -113,6 +113,14 @@ revision = "7c29201646fa3de8506f701213473dd407f19646" version = "v0.3.7" +[[projects]] + digest = "1:9cf73012dbe62f96cd8cdd210a1123a360401967a4530284dcd2990888747e3d" + name = "github.com/integrii/flaggy" + packages = ["."] + pruneopts = "NUT" + revision = "07ea7eb774047dab1bdcc7cad0f825fa419df47d" + version = "1.2.1" + [[projects]] branch = "master" digest = "1:7b2ab5c6de135d57d3a594b53068e2712da4d376b2f78097dec7b46d18deaa78" @@ -327,6 +335,7 @@ "github.com/fatih/color", "github.com/go-errors/errors", "github.com/imdario/mergo", + "github.com/integrii/flaggy", "github.com/jesseduffield/asciigraph", "github.com/jesseduffield/gocui", "github.com/jesseduffield/pty", diff --git a/vendor/github.com/integrii/flaggy/LICENSE b/vendor/github.com/integrii/flaggy/LICENSE new file mode 100644 index 00000000..cf1ab25d --- /dev/null +++ b/vendor/github.com/integrii/flaggy/LICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/vendor/github.com/integrii/flaggy/argumentParser.go b/vendor/github.com/integrii/flaggy/argumentParser.go new file mode 100644 index 00000000..089f2c5c --- /dev/null +++ b/vendor/github.com/integrii/flaggy/argumentParser.go @@ -0,0 +1,29 @@ +package flaggy + +// setValueForParsers sets the value for a specified key in the +// specified parsers (which normally include a Parser and Subcommand). +// The return values represent the key being set, and any errors +// returned when setting the key, such as failures to convert the string +// into the appropriate flag value. We stop assigning values as soon +// as we find a parser that accepts it. +func setValueForParsers(key string, value string, parsers ...ArgumentParser) (bool, error) { + + var valueWasSet bool + + for _, p := range parsers { + valueWasSet, err := p.SetValueForKey(key, value) + if err != nil { + return valueWasSet, err + } + if valueWasSet { + break + } + } + + return valueWasSet, nil +} + +// ArgumentParser represents a parser or subcommand +type ArgumentParser interface { + SetValueForKey(key string, value string) (bool, error) +} diff --git a/vendor/github.com/integrii/flaggy/flag.go b/vendor/github.com/integrii/flaggy/flag.go new file mode 100644 index 00000000..409ddf94 --- /dev/null +++ b/vendor/github.com/integrii/flaggy/flag.go @@ -0,0 +1,622 @@ +package flaggy + +import ( + "errors" + "fmt" + "net" + "reflect" + "strconv" + "strings" + "time" +) + +// Flag holds the base methods for all flag types +type Flag struct { + ShortName string + LongName string + Description string + rawValue string // the value as a string before being parsed + Hidden bool // indicates this flag should be hidden from help and suggestions + AssignmentVar interface{} + defaultValue string // the value (as a string), that was set by default before any parsing and assignment + parsed bool // indicates that this flag has already been parsed +} + +// HasName indicates that this flag's short or long name matches the +// supplied name string +func (f *Flag) HasName(name string) bool { + name = strings.TrimSpace(name) + if f.ShortName == name || f.LongName == name { + return true + } + return false +} + +// identifyAndAssignValue identifies the type of the incoming value +// and assigns it to the AssignmentVar pointer's target value. If +// the value is a type that needs parsing, that is performed as well. +func (f *Flag) identifyAndAssignValue(value string) error { + + var err error + + // Only parse this flag default value once. This keeps us from + // overwriting the default value in help output + if !f.parsed { + f.parsed = true + // parse the default value as a string and remember it for help output + f.defaultValue, err = f.returnAssignmentVarValueAsString() + if err != nil { + return err + } + } + + debugPrint("attempting to assign value", value, "to flag", f.LongName) + f.rawValue = value // remember the raw value + + // depending on the type of the assignment variable, we convert the + // incoming string and assign it. We only use pointers to variables + // in flagy. No returning vars by value. + switch f.AssignmentVar.(type) { + case *string: + v, _ := (f.AssignmentVar).(*string) + *v = value + case *[]string: + v := f.AssignmentVar.(*[]string) + splitString := strings.Split(value, ",") + new := append(*v, splitString...) + *v = new + case *bool: + v, err := strconv.ParseBool(value) + if err != nil { + return err + } + a, _ := (f.AssignmentVar).(*bool) + *a = v + case *[]bool: + // parse the incoming bool + b, err := strconv.ParseBool(value) + if err != nil { + return err + } + // cast the assignment var + existing := f.AssignmentVar.(*[]bool) + // deref the assignment var and append to it + v := append(*existing, b) + // pointer the new value and assign it + a, _ := (f.AssignmentVar).(*[]bool) + *a = v + case *time.Duration: + v, err := time.ParseDuration(value) + if err != nil { + return err + } + a, _ := (f.AssignmentVar).(*time.Duration) + *a = v + case *[]time.Duration: + t, err := time.ParseDuration(value) + if err != nil { + return err + } + existing := f.AssignmentVar.(*[]time.Duration) + // deref the assignment var and append to it + v := append(*existing, t) + // pointer the new value and assign it + a, _ := (f.AssignmentVar).(*[]time.Duration) + *a = v + case *float32: + v, err := strconv.ParseFloat(value, 32) + if err != nil { + return err + } + float := float32(v) + a, _ := (f.AssignmentVar).(*float32) + *a = float + case *[]float32: + v, err := strconv.ParseFloat(value, 32) + if err != nil { + return err + } + float := float32(v) + existing := f.AssignmentVar.(*[]float32) + new := append(*existing, float) + *existing = new + case *float64: + v, err := strconv.ParseFloat(value, 64) + if err != nil { + return err + } + a, _ := (f.AssignmentVar).(*float64) + *a = v + case *[]float64: + v, err := strconv.ParseFloat(value, 64) + if err != nil { + return err + } + existing := f.AssignmentVar.(*[]float64) + new := append(*existing, v) + + *existing = new + case *int: + v, err := strconv.Atoi(value) + if err != nil { + return err + } + e := f.AssignmentVar.(*int) + *e = v + case *[]int: + v, err := strconv.Atoi(value) + if err != nil { + return err + } + existing := f.AssignmentVar.(*[]int) + new := append(*existing, v) + *existing = new + case *uint: + v, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return err + } + existing := f.AssignmentVar.(*uint) + *existing = uint(v) + case *[]uint: + v, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return err + } + existing := f.AssignmentVar.(*[]uint) + new := append(*existing, uint(v)) + *existing = new + case *uint64: + v, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return err + } + existing := f.AssignmentVar.(*uint64) + *existing = v + case *[]uint64: + v, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return err + } + existing := f.AssignmentVar.(*[]uint64) + new := append(*existing, v) + *existing = new + case *uint32: + v, err := strconv.ParseUint(value, 10, 32) + if err != nil { + return err + } + existing := f.AssignmentVar.(*uint32) + *existing = uint32(v) + case *[]uint32: + v, err := strconv.ParseUint(value, 10, 32) + if err != nil { + return err + } + existing := f.AssignmentVar.(*[]uint32) + new := append(*existing, uint32(v)) + *existing = new + case *uint16: + v, err := strconv.ParseUint(value, 10, 16) + if err != nil { + return err + } + val := uint16(v) + existing := f.AssignmentVar.(*uint16) + *existing = val + case *[]uint16: + v, err := strconv.ParseUint(value, 10, 16) + if err != nil { + return err + } + existing := f.AssignmentVar.(*[]uint16) + new := append(*existing, uint16(v)) + *existing = new + case *uint8: + v, err := strconv.ParseUint(value, 10, 8) + if err != nil { + return err + } + val := uint8(v) + existing := f.AssignmentVar.(*uint8) + *existing = val + case *[]uint8: + var newSlice []uint8 + + v, err := strconv.ParseUint(value, 10, 8) + if err != nil { + return err + } + newV := uint8(v) + existing := f.AssignmentVar.(*[]uint8) + newSlice = append(*existing, newV) + *existing = newSlice + case *int64: + v, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return err + } + existing := f.AssignmentVar.(*int64) + *existing = v + case *[]int64: + v, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return err + } + existingSlice := f.AssignmentVar.(*[]int64) + newSlice := append(*existingSlice, v) + *existingSlice = newSlice + case *int32: + v, err := strconv.ParseInt(value, 10, 32) + if err != nil { + return err + } + converted := int32(v) + existing := f.AssignmentVar.(*int32) + *existing = converted + case *[]int32: + v, err := strconv.ParseInt(value, 10, 32) + if err != nil { + return err + } + existingSlice := f.AssignmentVar.(*[]int32) + newSlice := append(*existingSlice, int32(v)) + *existingSlice = newSlice + case *int16: + v, err := strconv.ParseInt(value, 10, 16) + if err != nil { + return err + } + converted := int16(v) + existing := f.AssignmentVar.(*int16) + *existing = converted + case *[]int16: + v, err := strconv.ParseInt(value, 10, 16) + if err != nil { + return err + } + existingSlice := f.AssignmentVar.(*[]int16) + newSlice := append(*existingSlice, int16(v)) + *existingSlice = newSlice + case *int8: + v, err := strconv.ParseInt(value, 10, 8) + if err != nil { + return err + } + converted := int8(v) + existing := f.AssignmentVar.(*int8) + *existing = converted + case *[]int8: + v, err := strconv.ParseInt(value, 10, 8) + if err != nil { + return err + } + existingSlice := f.AssignmentVar.(*[]int8) + newSlice := append(*existingSlice, int8(v)) + *existingSlice = newSlice + case *net.IP: + v := net.ParseIP(value) + existing := f.AssignmentVar.(*net.IP) + *existing = v + case *[]net.IP: + v := net.ParseIP(value) + existing := f.AssignmentVar.(*[]net.IP) + new := append(*existing, v) + *existing = new + case *net.HardwareAddr: + v, err := net.ParseMAC(value) + if err != nil { + return err + } + existing := f.AssignmentVar.(*net.HardwareAddr) + *existing = v + case *[]net.HardwareAddr: + v, err := net.ParseMAC(value) + if err != nil { + return err + } + existing := f.AssignmentVar.(*[]net.HardwareAddr) + new := append(*existing, v) + *existing = new + case *net.IPMask: + v := net.IPMask(net.ParseIP(value).To4()) + existing := f.AssignmentVar.(*net.IPMask) + *existing = v + case *[]net.IPMask: + v := net.IPMask(net.ParseIP(value).To4()) + existing := f.AssignmentVar.(*[]net.IPMask) + new := append(*existing, v) + *existing = new + default: + return errors.New("Unknown flag assignmentVar supplied in flag " + f.LongName + " " + f.ShortName) + } + + return err +} + +const argIsPositional = "positional" // subcommand or positional value +const argIsFlagWithSpace = "flagWithSpace" // -f path or --file path +const argIsFlagWithValue = "flagWithValue" // -f=path or --file=path +const argIsFinal = "final" // the final argument only '--' + +// determineArgType determines if the specified arg is a flag with space +// separated value, a flag with a connected value, or neither (positional) +func determineArgType(arg string) string { + + // if the arg is --, then its the final arg + if arg == "--" { + return argIsFinal + } + + // if it has the prefix --, then its a long flag + if strings.HasPrefix(arg, "--") { + // if it contains an equals, it is a joined value + if strings.Contains(arg, "=") { + return argIsFlagWithValue + } + return argIsFlagWithSpace + } + + // if it has the prefix -, then its a short flag + if strings.HasPrefix(arg, "-") { + // if it contains an equals, it is a joined value + if strings.Contains(arg, "=") { + return argIsFlagWithValue + } + return argIsFlagWithSpace + } + + return argIsPositional +} + +// parseArgWithValue parses a key=value concatenated argument into a key and +// value +func parseArgWithValue(arg string) (key string, value string) { + + // remove up to two minuses from start of flag + arg = strings.TrimPrefix(arg, "-") + arg = strings.TrimPrefix(arg, "-") + + // debugPrint("parseArgWithValue parsing", arg) + + // break at the equals + args := strings.SplitN(arg, "=", 2) + + // if its a bool arg, with no explicit value, we return a blank + if len(args) == 1 { + return args[0], "" + } + + // if its a key and value pair, we return those + if len(args) == 2 { + // debugPrint("parseArgWithValue parsed", args[0], args[1]) + return args[0], args[1] + } + + fmt.Println("Warning: attempted to parseArgWithValue but did not have correct parameter count.", arg, "->", args) + return "", "" +} + +// parseFlagToName parses a flag with space value down to a key name: +// --path -> path +// -p -> p +func parseFlagToName(arg string) string { + // remove minus from start + arg = strings.TrimLeft(arg, "-") + arg = strings.TrimLeft(arg, "-") + return arg +} + +// flagIsBool determines if the flag is a bool within the specified parser +// and subcommand's context +func flagIsBool(sc *Subcommand, p *Parser, key string) bool { + for _, f := range append(sc.Flags, p.Flags...) { + if f.HasName(key) { + _, isBool := f.AssignmentVar.(*bool) + _, isBoolSlice := f.AssignmentVar.(*[]bool) + if isBool || isBoolSlice { + return true + } + } + } + + // by default, the answer is false + return false +} + +// returnAssignmentVarValueAsString returns the value of the flag's +// assignment variable as a string. This is used to display the +// default value of flags before they are assigned (like when help is output). +func (f *Flag) returnAssignmentVarValueAsString() (string, error) { + + debugPrint("returning current value of assignment var of flag", f.LongName) + + var err error + + // depending on the type of the assignment variable, we convert the + // incoming string and assign it. We only use pointers to variables + // in flagy. No returning vars by value. + switch f.AssignmentVar.(type) { + case *string: + v, _ := (f.AssignmentVar).(*string) + return *v, err + case *[]string: + v := f.AssignmentVar.(*[]string) + return strings.Join(*v, ","), err + case *bool: + a, _ := (f.AssignmentVar).(*bool) + return strconv.FormatBool(*a), err + case *[]bool: + value := f.AssignmentVar.(*[]bool) + var ss []string + for _, b := range *value { + ss = append(ss, strconv.FormatBool(b)) + } + return strings.Join(ss, ","), err + case *time.Duration: + a := f.AssignmentVar.(*time.Duration) + return (*a).String(), err + case *[]time.Duration: + tds := f.AssignmentVar.(*[]time.Duration) + var asSlice []string + for _, td := range *tds { + asSlice = append(asSlice, td.String()) + } + return strings.Join(asSlice, ","), err + case *float32: + a := f.AssignmentVar.(*float32) + return strconv.FormatFloat(float64(*a), 'f', 2, 32), err + case *[]float32: + v := f.AssignmentVar.(*[]float32) + var strSlice []string + for _, f := range *v { + formatted := strconv.FormatFloat(float64(f), 'f', 2, 32) + strSlice = append(strSlice, formatted) + } + return strings.Join(strSlice, ","), err + case *float64: + a := f.AssignmentVar.(*float64) + return strconv.FormatFloat(float64(*a), 'f', 2, 64), err + case *[]float64: + v := f.AssignmentVar.(*[]float64) + var strSlice []string + for _, f := range *v { + formatted := strconv.FormatFloat(float64(f), 'f', 2, 64) + strSlice = append(strSlice, formatted) + } + return strings.Join(strSlice, ","), err + case *int: + a := f.AssignmentVar.(*int) + return strconv.Itoa(*a), err + case *[]int: + val := f.AssignmentVar.(*[]int) + var strSlice []string + for _, i := range *val { + str := strconv.Itoa(i) + strSlice = append(strSlice, str) + } + return strings.Join(strSlice, ","), err + case *uint: + v := f.AssignmentVar.(*uint) + return strconv.FormatUint(uint64(*v), 10), err + case *[]uint: + values := f.AssignmentVar.(*[]uint) + var strVars []string + for _, i := range *values { + strVars = append(strVars, strconv.FormatUint(uint64(i), 10)) + } + return strings.Join(strVars, ","), err + case *uint64: + v := f.AssignmentVar.(*uint64) + return strconv.FormatUint(*v, 10), err + case *[]uint64: + values := f.AssignmentVar.(*[]uint64) + var strVars []string + for _, i := range *values { + strVars = append(strVars, strconv.FormatUint(i, 10)) + } + return strings.Join(strVars, ","), err + case *uint32: + v := f.AssignmentVar.(*uint32) + return strconv.FormatUint(uint64(*v), 10), err + case *[]uint32: + values := f.AssignmentVar.(*[]uint32) + var strVars []string + for _, i := range *values { + strVars = append(strVars, strconv.FormatUint(uint64(i), 10)) + } + return strings.Join(strVars, ","), err + case *uint16: + v := f.AssignmentVar.(*uint16) + return strconv.FormatUint(uint64(*v), 10), err + case *[]uint16: + values := f.AssignmentVar.(*[]uint16) + var strVars []string + for _, i := range *values { + strVars = append(strVars, strconv.FormatUint(uint64(i), 10)) + } + return strings.Join(strVars, ","), err + case *uint8: + v := f.AssignmentVar.(*uint8) + return strconv.FormatUint(uint64(*v), 10), err + case *[]uint8: + values := f.AssignmentVar.(*[]uint8) + var strVars []string + for _, i := range *values { + strVars = append(strVars, strconv.FormatUint(uint64(i), 10)) + } + return strings.Join(strVars, ","), err + case *int64: + v := f.AssignmentVar.(*int64) + return strconv.FormatInt(int64(*v), 10), err + case *[]int64: + values := f.AssignmentVar.(*[]int64) + var strVars []string + for _, i := range *values { + strVars = append(strVars, strconv.FormatInt(i, 10)) + } + return strings.Join(strVars, ","), err + case *int32: + v := f.AssignmentVar.(*int32) + return strconv.FormatInt(int64(*v), 10), err + case *[]int32: + values := f.AssignmentVar.(*[]int32) + var strVars []string + for _, i := range *values { + strVars = append(strVars, strconv.FormatInt(int64(i), 10)) + } + return strings.Join(strVars, ","), err + case *int16: + v := f.AssignmentVar.(*int16) + return strconv.FormatInt(int64(*v), 10), err + case *[]int16: + values := f.AssignmentVar.(*[]int16) + var strVars []string + for _, i := range *values { + strVars = append(strVars, strconv.FormatInt(int64(i), 10)) + } + return strings.Join(strVars, ","), err + case *int8: + v := f.AssignmentVar.(*int8) + return strconv.FormatInt(int64(*v), 10), err + case *[]int8: + values := f.AssignmentVar.(*[]int8) + var strVars []string + for _, i := range *values { + strVars = append(strVars, strconv.FormatInt(int64(i), 10)) + } + return strings.Join(strVars, ","), err + case *net.IP: + val := f.AssignmentVar.(*net.IP) + return val.String(), err + case *[]net.IP: + val := f.AssignmentVar.(*[]net.IP) + var strSlice []string + for _, ip := range *val { + strSlice = append(strSlice, ip.String()) + } + return strings.Join(strSlice, ","), err + case *net.HardwareAddr: + val := f.AssignmentVar.(*net.HardwareAddr) + return val.String(), err + case *[]net.HardwareAddr: + val := f.AssignmentVar.(*[]net.HardwareAddr) + var strSlice []string + for _, mac := range *val { + strSlice = append(strSlice, mac.String()) + } + return strings.Join(strSlice, ","), err + case *net.IPMask: + val := f.AssignmentVar.(*net.IPMask) + return val.String(), err + case *[]net.IPMask: + val := f.AssignmentVar.(*[]net.IPMask) + var strSlice []string + for _, m := range *val { + strSlice = append(strSlice, m.String()) + } + return strings.Join(strSlice, ","), err + default: + return "", errors.New("Unknown flag assignmentVar found in flag " + f.LongName + " " + f.ShortName + ". Type not supported: " + reflect.TypeOf(f.AssignmentVar).String()) + } +} diff --git a/vendor/github.com/integrii/flaggy/help.go b/vendor/github.com/integrii/flaggy/help.go new file mode 100644 index 00000000..cdbb02b2 --- /dev/null +++ b/vendor/github.com/integrii/flaggy/help.go @@ -0,0 +1,23 @@ +package flaggy + +// defaultHelpTemplate is the help template used by default +// {{if (or (or (gt (len .StringFlags) 0) (gt (len .IntFlags) 0)) (gt (len .BoolFlags) 0))}} +// {{if (or (gt (len .StringFlags) 0) (gt (len .BoolFlags) 0))}} +const defaultHelpTemplate = `{{.CommandName}}{{if .Description}} - {{.Description}}{{end}}{{if .PrependMessage}} +{{.PrependMessage}}{{end}} +{{if .UsageString}} + Usage: + {{.UsageString}}{{end}}{{if .Positionals}} + + Positional Variables: {{range .Positionals}} + {{.Name}}{{if .Required}} (Required){{end}}{{if .Description}} - {{.Description}}{{end}}{{if .DefaultValue}} (default: {{.DefaultValue}}){{end}}{{end}}{{end}}{{if .Subcommands}} + + Subcommands: {{range .Subcommands}} + {{.LongName}}{{if .ShortName}} ({{.ShortName}}){{end}}{{if .Position}}{{if gt .Position 1}} (position {{.Position}}){{end}}{{end}}{{if .Description}} - {{.Description}}{{end}}{{end}} +{{end}}{{if (gt (len .Flags) 0)}} + Flags: {{if .Flags}}{{range .Flags}} + {{if .ShortName}}-{{.ShortName}} {{else}} {{end}}{{if .LongName}}--{{.LongName}} {{end}}{{if .Description}} {{.Description}}{{if .DefaultValue}} (default: {{.DefaultValue}}){{end}}{{end}}{{end}}{{end}} +{{end}}{{if .AppendMessage}}{{.AppendMessage}} +{{end}}{{if .Message}} +{{.Message}}{{end}} +` diff --git a/vendor/github.com/integrii/flaggy/helpValues.go b/vendor/github.com/integrii/flaggy/helpValues.go new file mode 100644 index 00000000..8a5146b1 --- /dev/null +++ b/vendor/github.com/integrii/flaggy/helpValues.go @@ -0,0 +1,215 @@ +package flaggy + +// Help represents the values needed to render a Help page +type Help struct { + Subcommands []HelpSubcommand + Positionals []HelpPositional + Flags []HelpFlag + UsageString string + CommandName string + PrependMessage string + AppendMessage string + Message string + Description string +} + +// HelpSubcommand is used to template subcommand Help output +type HelpSubcommand struct { + ShortName string + LongName string + Description string + Position int +} + +// HelpPositional is used to template positional Help output +type HelpPositional struct { + Name string + Description string + Required bool + Position int + DefaultValue string +} + +// HelpFlag is used to template string flag Help output +type HelpFlag struct { + ShortName string + LongName string + Description string + DefaultValue string +} + +// ExtractValues extracts Help template values from a subcommand and its parent +// parser. The parser is required in order to detect default flag settings +// for help and version outut. +func (h *Help) ExtractValues(p *Parser, message string) { + + // accept message string for output + h.Message = message + + // extract Help values from the current subcommand in context + // prependMessage string + h.PrependMessage = p.subcommandContext.AdditionalHelpPrepend + // appendMessage string + h.AppendMessage = p.subcommandContext.AdditionalHelpAppend + // command name + h.CommandName = p.subcommandContext.Name + // description + h.Description = p.subcommandContext.Description + + // subcommands []HelpSubcommand + for _, cmd := range p.subcommandContext.Subcommands { + if cmd.Hidden { + continue + } + newHelpSubcommand := HelpSubcommand{ + ShortName: cmd.ShortName, + LongName: cmd.Name, + Description: cmd.Description, + Position: cmd.Position, + } + h.Subcommands = append(h.Subcommands, newHelpSubcommand) + } + + // parse positional flags into help output structs + for _, pos := range p.subcommandContext.PositionalFlags { + if pos.Hidden { + continue + } + newHelpPositional := HelpPositional{ + Name: pos.Name, + Position: pos.Position, + Description: pos.Description, + Required: pos.Required, + DefaultValue: pos.defaultValue, + } + h.Positionals = append(h.Positionals, newHelpPositional) + } + + // if the built-in version flag is enabled, then add it as a help flag + if p.ShowVersionWithVersionFlag { + defaultVersionFlag := HelpFlag{ + ShortName: "", + LongName: versionFlagLongName, + Description: "Displays the program version string.", + DefaultValue: "", + } + h.Flags = append(h.Flags, defaultVersionFlag) + } + + // if the built-in help flag exists, then add it as a help flag + if p.ShowHelpWithHFlag { + defaultHelpFlag := HelpFlag{ + ShortName: helpFlagShortName, + LongName: helpFlagLongName, + Description: "Displays help with available flag, subcommand, and positional value parameters.", + DefaultValue: "", + } + h.Flags = append(h.Flags, defaultHelpFlag) + } + + // go through every flag in the subcommand and add it to help output + h.parseFlagsToHelpFlags(p.subcommandContext.Flags) + + // go through every flag in the parent parser and add it to help output + h.parseFlagsToHelpFlags(p.Flags) + + // formulate the usage string + // first, we capture all the command and positional names by position + commandsByPosition := make(map[int]string) + for _, pos := range p.subcommandContext.PositionalFlags { + if pos.Hidden { + continue + } + if len(commandsByPosition[pos.Position]) > 0 { + commandsByPosition[pos.Position] = commandsByPosition[pos.Position] + "|" + pos.Name + } else { + commandsByPosition[pos.Position] = pos.Name + } + } + for _, cmd := range p.subcommandContext.Subcommands { + if cmd.Hidden { + continue + } + if len(commandsByPosition[cmd.Position]) > 0 { + commandsByPosition[cmd.Position] = commandsByPosition[cmd.Position] + "|" + cmd.Name + } else { + commandsByPosition[cmd.Position] = cmd.Name + } + } + + // find the highest position count in the map + var highestPosition int + for i := range commandsByPosition { + if i > highestPosition { + highestPosition = i + } + } + + // only have a usage string if there are positional items + var usageString string + if highestPosition > 0 { + // find each positional value and make our final string + usageString = p.subcommandContext.Name + for i := 1; i <= highestPosition; i++ { + if len(commandsByPosition[i]) > 0 { + usageString = usageString + " [" + commandsByPosition[i] + "]" + } else { + // dont keep listing after the first position without any properties + // it will be impossible to reach anything beyond here anyway + break + } + } + } + + h.UsageString = usageString + +} + +// parseFlagsToHelpFlags parses the specified slice of flags into +// help flags on the the calling help command +func (h *Help) parseFlagsToHelpFlags(flags []*Flag) { + for _, f := range flags { + if f.Hidden { + continue + } + + // determine the default value based on the assignment variable + defaultValue := f.defaultValue + + // dont show nils + if defaultValue == "" { + defaultValue = "" + } + + // for bools, dont show a default of false + _, isBool := f.AssignmentVar.(*bool) + if isBool { + b := f.AssignmentVar.(*bool) + if *b == false { + defaultValue = "" + } + } + + newHelpFlag := HelpFlag{ + ShortName: f.ShortName, + LongName: f.LongName, + Description: f.Description, + DefaultValue: defaultValue, + } + + h.AddFlagToHelp(newHelpFlag) + } +} + +// AddFlagToHelp adds a flag to help output if it does not exist +func (h *Help) AddFlagToHelp(f HelpFlag) { + for _, existingFlag := range h.Flags { + if len(existingFlag.ShortName) > 0 && existingFlag.ShortName == f.ShortName { + return + } + if len(existingFlag.LongName) > 0 && existingFlag.LongName == f.LongName { + return + } + } + h.Flags = append(h.Flags, f) +} diff --git a/vendor/github.com/integrii/flaggy/main.go b/vendor/github.com/integrii/flaggy/main.go new file mode 100644 index 00000000..b44cd915 --- /dev/null +++ b/vendor/github.com/integrii/flaggy/main.go @@ -0,0 +1,341 @@ +// Package flaggy is a input flag parsing package that supports recursive +// subcommands, positional values, and any-position flags without +// unnecessary complexeties. +// +// For a getting started tutorial and full feature list, check out the +// readme at https://github.com/integrii/flaggy. +package flaggy // import "github.com/integrii/flaggy" + +import ( + "fmt" + "log" + "net" + "os" + "strconv" + "strings" + "time" +) + +// strings used for builtin help and version flags both short and long +const versionFlagLongName = "version" +const helpFlagLongName = "help" +const helpFlagShortName = "h" + +// defaultVersion is applied to parsers when they are created +const defaultVersion = "0.0.0" + +// DebugMode indicates that debug output should be enabled +var DebugMode bool + +// DefaultHelpTemplate is the help template that will be used +// for newly created subcommands and commands +var DefaultHelpTemplate = defaultHelpTemplate + +// DefaultParser is the default parser that is used with the package-level public +// functions +var DefaultParser *Parser + +// TrailingArguments holds trailing arguments in the main parser after parsing +// has been run. +var TrailingArguments []string + +func init() { + + // set the default help template + // allow usage like flaggy.StringVar by enabling a default Parser + ResetParser() +} + +// ResetParser resets the default parser to a fresh instance. Uses the +// name of the binary executing as the program name by default. +func ResetParser() { + if len(os.Args) > 0 { + chunks := strings.Split(os.Args[0], "/") + DefaultParser = NewParser(chunks[len(chunks)-1]) + } else { + DefaultParser = NewParser("default") + } +} + +// Parse parses flags as requested in the default package parser +func Parse() { + err := DefaultParser.Parse() + TrailingArguments = DefaultParser.TrailingArguments + if err != nil { + log.Panicln("Error from argument parser:", err) + } +} + +// ParseArgs parses the passed args as if they were the arguments to the +// running binary. Targets the default main parser for the package. +func ParseArgs(args []string) { + err := DefaultParser.ParseArgs(args) + TrailingArguments = DefaultParser.TrailingArguments + if err != nil { + log.Panicln("Error from argument parser:", err) + } +} + +// String adds a new string flag +func String(assignmentVar *string, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// StringSlice adds a new slice of strings flag +// Specify the flag multiple times to fill the slice +func StringSlice(assignmentVar *[]string, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// Bool adds a new bool flag +func Bool(assignmentVar *bool, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// BoolSlice adds a new slice of bools flag +// Specify the flag multiple times to fill the slice +func BoolSlice(assignmentVar *[]bool, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// ByteSlice adds a new slice of bytes flag +// Specify the flag multiple times to fill the slice. Takes hex as input. +func ByteSlice(assignmentVar *[]byte, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// Duration adds a new time.Duration flag. +// Input format is described in time.ParseDuration(). +// Example values: 1h, 1h50m, 32s +func Duration(assignmentVar *time.Duration, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// DurationSlice adds a new time.Duration flag. +// Input format is described in time.ParseDuration(). +// Example values: 1h, 1h50m, 32s +// Specify the flag multiple times to fill the slice. +func DurationSlice(assignmentVar *[]time.Duration, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// Float32 adds a new float32 flag. +func Float32(assignmentVar *float32, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// Float32Slice adds a new float32 flag. +// Specify the flag multiple times to fill the slice. +func Float32Slice(assignmentVar *[]float32, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// Float64 adds a new float64 flag. +func Float64(assignmentVar *float64, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// Float64Slice adds a new float64 flag. +// Specify the flag multiple times to fill the slice. +func Float64Slice(assignmentVar *[]float64, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// Int adds a new int flag +func Int(assignmentVar *int, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// IntSlice adds a new int slice flag. +// Specify the flag multiple times to fill the slice. +func IntSlice(assignmentVar *[]int, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// UInt adds a new uint flag +func UInt(assignmentVar *uint, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// UIntSlice adds a new uint slice flag. +// Specify the flag multiple times to fill the slice. +func UIntSlice(assignmentVar *[]uint, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// UInt64 adds a new uint64 flag +func UInt64(assignmentVar *uint64, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// UInt64Slice adds a new uint64 slice flag. +// Specify the flag multiple times to fill the slice. +func UInt64Slice(assignmentVar *[]uint64, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// UInt32 adds a new uint32 flag +func UInt32(assignmentVar *uint32, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// UInt32Slice adds a new uint32 slice flag. +// Specify the flag multiple times to fill the slice. +func UInt32Slice(assignmentVar *[]uint32, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// UInt16 adds a new uint16 flag +func UInt16(assignmentVar *uint16, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// UInt16Slice adds a new uint16 slice flag. +// Specify the flag multiple times to fill the slice. +func UInt16Slice(assignmentVar *[]uint16, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// UInt8 adds a new uint8 flag +func UInt8(assignmentVar *uint8, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// UInt8Slice adds a new uint8 slice flag. +// Specify the flag multiple times to fill the slice. +func UInt8Slice(assignmentVar *[]uint8, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// Int64 adds a new int64 flag +func Int64(assignmentVar *int64, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// Int64Slice adds a new int64 slice flag. +// Specify the flag multiple times to fill the slice. +func Int64Slice(assignmentVar *[]int64, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// Int32 adds a new int32 flag +func Int32(assignmentVar *int32, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// Int32Slice adds a new int32 slice flag. +// Specify the flag multiple times to fill the slice. +func Int32Slice(assignmentVar *[]int32, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// Int16 adds a new int16 flag +func Int16(assignmentVar *int16, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// Int16Slice adds a new int16 slice flag. +// Specify the flag multiple times to fill the slice. +func Int16Slice(assignmentVar *[]int16, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// Int8 adds a new int8 flag +func Int8(assignmentVar *int8, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// Int8Slice adds a new int8 slice flag. +// Specify the flag multiple times to fill the slice. +func Int8Slice(assignmentVar *[]int8, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// IP adds a new net.IP flag. +func IP(assignmentVar *net.IP, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// IPSlice adds a new int8 slice flag. +// Specify the flag multiple times to fill the slice. +func IPSlice(assignmentVar *[]net.IP, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// HardwareAddr adds a new net.HardwareAddr flag. +func HardwareAddr(assignmentVar *net.HardwareAddr, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// HardwareAddrSlice adds a new net.HardwareAddr slice flag. +// Specify the flag multiple times to fill the slice. +func HardwareAddrSlice(assignmentVar *[]net.HardwareAddr, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// IPMask adds a new net.IPMask flag. IPv4 Only. +func IPMask(assignmentVar *net.IPMask, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// IPMaskSlice adds a new net.HardwareAddr slice flag. IPv4 only. +// Specify the flag multiple times to fill the slice. +func IPMaskSlice(assignmentVar *[]net.IPMask, shortName string, longName string, description string) { + DefaultParser.add(assignmentVar, shortName, longName, description) +} + +// AttachSubcommand adds a subcommand for parsing +func AttachSubcommand(subcommand *Subcommand, relativePosition int) { + DefaultParser.AttachSubcommand(subcommand, relativePosition) +} + +// ShowHelp shows parser help +func ShowHelp(message string) { + DefaultParser.ShowHelpWithMessage(message) +} + +// SetDescription sets the description of the default package command parser +func SetDescription(description string) { + DefaultParser.Description = description +} + +// SetVersion sets the version of the default package command parser +func SetVersion(version string) { + DefaultParser.Version = version +} + +// SetName sets the name of the default package command parser +func SetName(name string) { + DefaultParser.Name = name +} + +// ShowHelpAndExit shows parser help and exits with status code 2 +func ShowHelpAndExit(message string) { + ShowHelp(message) + exitOrPanic(2) +} + +// PanicInsteadOfExit is used when running tests +var PanicInsteadOfExit bool + +// exitOrPanic panics instead of calling os.Exit so that tests can catch +// more failures +func exitOrPanic(code int) { + if PanicInsteadOfExit { + panic("Panic instead of exit with code: " + strconv.Itoa(code)) + } + os.Exit(code) +} + +// AddPositionalValue adds a positional value to the main parser at the global +// context +func AddPositionalValue(assignmentVar *string, name string, relativePosition int, required bool, description string) { + DefaultParser.AddPositionalValue(assignmentVar, name, relativePosition, required, description) +} + +// debugPrint prints if debugging is enabled +func debugPrint(i ...interface{}) { + if DebugMode { + fmt.Println(i...) + } +} diff --git a/vendor/github.com/integrii/flaggy/parser.go b/vendor/github.com/integrii/flaggy/parser.go new file mode 100644 index 00000000..06639326 --- /dev/null +++ b/vendor/github.com/integrii/flaggy/parser.go @@ -0,0 +1,111 @@ +package flaggy + +import ( + "errors" + "fmt" + "os" + + "text/template" +) + +// Parser represents the set of vars and subcommands we are expecting +// from our input args, and the parser than handles them all. +type Parser struct { + Subcommand + Version string // the optional version of the parser. + ShowHelpWithHFlag bool // display help when -h or --help passed + ShowVersionWithVersionFlag bool // display the version when --version passed + ShowHelpOnUnexpected bool // display help when an unexpected flag is passed + TrailingArguments []string // everything after a -- is placed here + HelpTemplate *template.Template // template for Help output + trailingArgumentsExtracted bool // indicates that trailing args have been parsed and should not be appended again + parsed bool // indicates this parser has parsed + subcommandContext *Subcommand // points to the most specific subcommand being used +} + +// NewParser creates a new ArgumentParser ready to parse inputs +func NewParser(name string) *Parser { + // this can not be done inline because of struct embedding + p := &Parser{} + p.Name = name + p.Version = defaultVersion + p.ShowHelpOnUnexpected = true + p.ShowHelpWithHFlag = true + p.ShowVersionWithVersionFlag = true + p.SetHelpTemplate(DefaultHelpTemplate) + p.subcommandContext = &Subcommand{} + return p +} + +// ParseArgs parses as if the passed args were the os.Args, but without the +// binary at the 0 position in the array. An error is returned if there +// is a low level issue converting flags to their proper type. No error +// is returned for invalid arguments or missing require subcommands. +func (p *Parser) ParseArgs(args []string) error { + if p.parsed { + return errors.New("Parser.Parse() called twice on parser with name: " + " " + p.Name + " " + p.ShortName) + } + p.parsed = true + // debugPrint("Kicking off parsing with args:", args) + return p.parse(p, args, 0) +} + +// ShowVersionAndExit shows the version of this parser +func (p *Parser) ShowVersionAndExit() { + fmt.Println("Version:", p.Version) + exitOrPanic(0) +} + +// SetHelpTemplate sets the go template this parser will use when rendering +// Help. +func (p *Parser) SetHelpTemplate(tmpl string) error { + var err error + p.HelpTemplate = template.New(helpFlagLongName) + p.HelpTemplate, err = p.HelpTemplate.Parse(tmpl) + if err != nil { + return err + } + return nil +} + +// Parse calculates all flags and subcommands +func (p *Parser) Parse() error { + + err := p.ParseArgs(os.Args[1:]) + if err != nil { + return err + } + return nil + +} + +// ShowHelp shows Help without an error message +func (p *Parser) ShowHelp() { + debugPrint("showing help for", p.subcommandContext.Name) + p.ShowHelpWithMessage("") +} + +// ShowHelpAndExit shows parser help and exits with status code 2 +func (p *Parser) ShowHelpAndExit(message string) { + p.ShowHelpWithMessage(message) + exitOrPanic(2) +} + +// ShowHelpWithMessage shows the Help for this parser with an optional string error +// message as a header. The supplied subcommand will be the context of Help +// displayed to the user. +func (p *Parser) ShowHelpWithMessage(message string) { + + // create a new Help values template and extract values into it + help := Help{} + help.ExtractValues(p, message) + err := p.HelpTemplate.Execute(os.Stderr, help) + if err != nil { + fmt.Fprintln(os.Stderr, "Error rendering Help template:", err) + } +} + +// Disable show version with --version. It is enabled by default. +func (p *Parser) DisableShowVersionWithVersion() { + p.ShowVersionWithVersionFlag = false +} diff --git a/vendor/github.com/integrii/flaggy/positionalValue.go b/vendor/github.com/integrii/flaggy/positionalValue.go new file mode 100644 index 00000000..bf649a9d --- /dev/null +++ b/vendor/github.com/integrii/flaggy/positionalValue.go @@ -0,0 +1,14 @@ +package flaggy + +// PositionalValue represents a value which is determined by its position +// relative to where a subcommand was detected. +type PositionalValue struct { + Name string // used in documentation only + Description string + AssignmentVar *string // the var that will get this variable + Position int // the position, not including switches, of this variable + Required bool // this subcommand must always be specified + Found bool // was this positional found during parsing? + Hidden bool // indicates this positional value should be hidden from help + defaultValue string // used for help output +} diff --git a/vendor/github.com/integrii/flaggy/subCommand.go b/vendor/github.com/integrii/flaggy/subCommand.go new file mode 100644 index 00000000..95d87295 --- /dev/null +++ b/vendor/github.com/integrii/flaggy/subCommand.go @@ -0,0 +1,691 @@ +package flaggy + +import ( + "fmt" + "log" + "net" + "os" + "strconv" + "strings" + "time" +) + +// Subcommand represents a subcommand which contains a set of child +// subcommands along with a set of flags relevant to it. Parsing +// runs until a subcommand is detected by matching its name and +// position. Once a matching subcommand is found, the next set +// of parsing occurs within that matched subcommand. +type Subcommand struct { + Name string + ShortName string + Description string + Position int // the position of this subcommand, not including flags + Subcommands []*Subcommand + Flags []*Flag + PositionalFlags []*PositionalValue + AdditionalHelpPrepend string // additional prepended message when Help is displayed + AdditionalHelpAppend string // additional appended message when Help is displayed + Used bool // indicates this subcommand was found and parsed + Hidden bool // indicates this subcommand should be hidden from help +} + +// NewSubcommand creates a new subcommand that can have flags or PositionalFlags +// added to it. The position starts with 1, not 0 +func NewSubcommand(name string) *Subcommand { + newSC := &Subcommand{ + Name: name, + } + return newSC +} + +// parseAllFlagsFromArgs parses the non-positional flags such as -f or -v=value +// out of the supplied args and returns the positional items in order. +func (sc *Subcommand) parseAllFlagsFromArgs(p *Parser, args []string) ([]string, bool, error) { + + var err error + var positionalOnlyArguments []string + var helpRequested bool // indicates the user has supplied -h and we + // should render help if we are the last subcommand + + // indicates we should skip the next argument, like when parsing a flag + // that separates key and value by space + var skipNext bool + + // endArgfound indicates that a -- was found and everything + // remaining should be added to the trailing arguments slices + var endArgFound bool + + // find all the normal flags (not positional) and parse them out + for i, a := range args { + + debugPrint("parsing arg", 1, a) + + // evaluate if there is a following arg to avoid panics + var nextArgExists bool + var nextArg string + if len(args)-1 >= i+1 { + nextArgExists = true + nextArg = args[i+1] + } + + // if end arg -- has been found, just add everything to TrailingArguments + if endArgFound { + if !p.trailingArgumentsExtracted { + p.TrailingArguments = append(p.TrailingArguments, a) + } + continue + } + + // skip this run if specified + if skipNext { + skipNext = false + debugPrint("skipping flag because it is an arg:", a) + continue + } + + // parse the flag into its name for consideration without dashes + flagName := parseFlagToName(a) + + // if the flag being passed is version or v and the option to display + // version with version flags, then display version + if p.ShowVersionWithVersionFlag { + if flagName == versionFlagLongName { + p.ShowVersionAndExit() + } + } + + // if the show Help on h flag option is set, then show Help when h or Help + // is passed as an option + if p.ShowHelpWithHFlag { + if flagName == helpFlagShortName || flagName == helpFlagLongName { + // Ensure this is the last subcommand passed so we give the correct + // help output + helpRequested = true + continue + } + } + + // determine what kind of flag this is + argType := determineArgType(a) + + // strip flags from arg + // debugPrint("Parsing flag named", a, "of type", argType) + + // depending on the flag type, parse the key and value out, then apply it + switch argType { + case argIsFinal: + // debugPrint("Arg", i, "is final:", a) + endArgFound = true + case argIsPositional: + // debugPrint("Arg is positional or subcommand:", a) + // this positional argument into a slice of their own, so that + // we can determine if its a subcommand or positional value later + positionalOnlyArguments = append(positionalOnlyArguments, a) + case argIsFlagWithSpace: + a = parseFlagToName(a) + // debugPrint("Arg", i, "is flag with space:", a) + // parse next arg as value to this flag and apply to subcommand flags + // if the flag is a bool flag, then we check for a following positional + // and skip it if necessary + if flagIsBool(sc, p, a) { + debugPrint(sc.Name, "bool flag", a, "next var is:", nextArg) + _, err = setValueForParsers(a, "true", p, sc) + + // if an error occurs, just return it and quit parsing + if err != nil { + return []string{}, false, err + } + // by default, we just assign the next argument to the value and continue + continue + } + + skipNext = true + debugPrint(sc.Name, "NOT bool flag", a) + + // if the next arg was not found, then show a Help message + if !nextArgExists { + p.ShowHelpWithMessage("Expected a following arg for flag " + a + ", but it did not exist.") + exitOrPanic(2) + } + _, err = setValueForParsers(a, nextArg, p, sc) + if err != nil { + return []string{}, false, err + } + case argIsFlagWithValue: + // debugPrint("Arg", i, "is flag with value:", a) + a = parseFlagToName(a) + // parse flag into key and value and apply to subcommand flags + key, val := parseArgWithValue(a) + _, err = setValueForParsers(key, val, p, sc) + if err != nil { + return []string{}, false, err + } + // if this flag type was found and not set, and the parser is set to show + // Help when an unknown flag is found, then show Help and exit. + } + + } + + return positionalOnlyArguments, helpRequested, nil +} + +// Parse causes the argument parser to parse based on the supplied []string. +// depth specifies the non-flag subcommand positional depth +func (sc *Subcommand) parse(p *Parser, args []string, depth int) error { + + debugPrint("- Parsing subcommand", sc.Name, "with depth of", depth, "and args", args) + + // if a command is parsed, its used + sc.Used = true + + // as subcommands are used, they become the context of the parser. This helps + // us understand how to display help based on which subcommand is being used + p.subcommandContext = sc + + // ensure that help and version flags are not used if the parser has the + // built-in help and version flags enabled + if p.ShowHelpWithHFlag { + sc.ensureNoConflictWithBuiltinHelp() + } + if p.ShowVersionWithVersionFlag { + sc.ensureNoConflictWithBuiltinVersion() + } + + // Parse the normal flags out of the argument list and retain the positionals. + // Apply the flags to the parent parser and the current subcommand context. + // ./command -f -z subcommand someVar -b becomes ./command subcommand somevar + positionalOnlyArguments, helpRequested, err := sc.parseAllFlagsFromArgs(p, args) + if err != nil { + return err + } + + // indicate that trailing arguments have been extracted, so that they aren't + // appended a second time + p.trailingArgumentsExtracted = true + + // loop over positional values and look for their matching positional + // parameter, or their positional command. If neither are found, then + // we throw an error + var parsedArgCount int + for pos, v := range positionalOnlyArguments { + + // the first relative positional argument will be human natural at position 1 + // but offset for the depth of relative commands being parsed for currently. + relativeDepth := pos - depth + 1 + // debugPrint("Parsing positional only position", relativeDepth, "with value", v) + + if relativeDepth < 1 { + // debugPrint(sc.Name, "skipped value:", v) + continue + } + parsedArgCount++ + + // determine subcommands and parse them by positional value and name + for _, cmd := range sc.Subcommands { + // debugPrint("Subcommand being compared", relativeDepth, "==", cmd.Position, "and", v, "==", cmd.Name, "==", cmd.ShortName) + if relativeDepth == cmd.Position && (v == cmd.Name || v == cmd.ShortName) { + debugPrint("Decending into positional subcommand", cmd.Name, "at relativeDepth", relativeDepth, "and absolute depth", depth+1) + return cmd.parse(p, args, depth+parsedArgCount) // continue recursive positional parsing + } + } + + // determine positional args and parse them by positional value and name + var foundPositional bool + for _, val := range sc.PositionalFlags { + if relativeDepth == val.Position { + debugPrint("Found a positional value at relativePos:", relativeDepth, "value:", v) + + // set original value for help output + val.defaultValue = *val.AssignmentVar + + // defrerence the struct pointer, then set the pointer property within it + *val.AssignmentVar = v + // debugPrint("set positional to value", *val.AssignmentVar) + foundPositional = true + val.Found = true + break + } + } + + // if there aren't any positional flags but there are subcommands that + // were not used, display a useful message with subcommand options. + if !foundPositional && p.ShowHelpOnUnexpected { + debugPrint("No positional at position", relativeDepth) + var foundSubcommandAtDepth bool + for _, cmd := range sc.Subcommands { + if cmd.Position == relativeDepth { + foundSubcommandAtDepth = true + } + } + + // if there is a subcommand here but it was not specified, display them all + // as a suggestion to the user before exiting. + if foundSubcommandAtDepth { + // determine which name to use in upcoming help output + fmt.Fprintln(os.Stderr, sc.Name+":", "No subcommand or positional value found at position", strconv.Itoa(relativeDepth)+".") + var output string + for _, cmd := range sc.Subcommands { + if cmd.Hidden { + continue + } + output = output + " " + cmd.Name + } + // if there are available subcommands, let the user know + if len(output) > 0 { + output = strings.TrimLeft(output, " ") + fmt.Println("Available subcommands:", output) + } + exitOrPanic(2) + } + + // if there were not any flags or subcommands at this position at all, then + // throw an error (display Help if necessary) + p.ShowHelpWithMessage("Unexpected argument: " + v) + exitOrPanic(2) + } + } + + // if help was requested and we should show help when h is passed, + if helpRequested && p.ShowHelpWithHFlag { + p.ShowHelp() + exitOrPanic(0) + } + + // find any positionals that were not used on subcommands that were + // found and throw help (unknown argument) + for _, pv := range p.PositionalFlags { + if pv.Required && !pv.Found { + p.ShowHelpWithMessage("Required global positional variable " + pv.Name + " not found at position " + strconv.Itoa(pv.Position)) + exitOrPanic(2) + } + } + for _, pv := range sc.PositionalFlags { + if pv.Required && !pv.Found { + p.ShowHelpWithMessage("Required positional of subcommand " + sc.Name + " named " + pv.Name + " not found at position " + strconv.Itoa(pv.Position)) + exitOrPanic(2) + } + } + + return nil +} + +// FlagExists lets you know if the flag name exists as either a short or long +// name in the (sub)command +func (sc *Subcommand) FlagExists(name string) bool { + + for _, f := range sc.Flags { + if f.HasName(name) { + return true + } + } + + return false +} + +// AttachSubcommand adds a possible subcommand to the Parser. +func (sc *Subcommand) AttachSubcommand(newSC *Subcommand, relativePosition int) { + + // assign the depth of the subcommand when its attached + newSC.Position = relativePosition + + // ensure no subcommands at this depth with this name + for _, other := range sc.Subcommands { + if newSC.Position == other.Position { + if newSC.Name != "" { + if newSC.Name == other.Name { + log.Panicln("Unable to add subcommand because one already exists at position" + strconv.Itoa(newSC.Position) + " with name " + other.Name) + } + } + if newSC.ShortName != "" { + if newSC.ShortName == other.ShortName { + log.Panicln("Unable to add subcommand because one already exists at position" + strconv.Itoa(newSC.Position) + " with name " + other.ShortName) + } + } + } + } + + // ensure no positionals at this depth + for _, other := range sc.PositionalFlags { + if newSC.Position == other.Position { + log.Panicln("Unable to add subcommand because a positional value already exists at position " + strconv.Itoa(newSC.Position) + ": " + other.Name) + } + } + + sc.Subcommands = append(sc.Subcommands, newSC) +} + +// add is a "generic" to add flags of any type. Checks the supplied parent +// parser to ensure that the user isn't setting version or help flags that +// conflict with the built-in help and version flag behavior. +func (sc *Subcommand) add(assignmentVar interface{}, shortName string, longName string, description string) { + + // if the flag is already used, throw an error + for _, existingFlag := range sc.Flags { + if longName != "" && existingFlag.LongName == longName { + log.Panicln("Flag " + longName + " added to subcommand " + sc.Name + " but the name is already assigned.") + } + if shortName != "" && existingFlag.ShortName == shortName { + log.Panicln("Flag " + shortName + " added to subcommand " + sc.Name + " but the short name is already assigned.") + } + } + + newFlag := Flag{ + AssignmentVar: assignmentVar, + ShortName: shortName, + LongName: longName, + Description: description, + } + sc.Flags = append(sc.Flags, &newFlag) +} + +// String adds a new string flag +func (sc *Subcommand) String(assignmentVar *string, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// StringSlice adds a new slice of strings flag +// Specify the flag multiple times to fill the slice +func (sc *Subcommand) StringSlice(assignmentVar *[]string, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// Bool adds a new bool flag +func (sc *Subcommand) Bool(assignmentVar *bool, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// BoolSlice adds a new slice of bools flag +// Specify the flag multiple times to fill the slice +func (sc *Subcommand) BoolSlice(assignmentVar *[]bool, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// ByteSlice adds a new slice of bytes flag +// Specify the flag multiple times to fill the slice. Takes hex as input. +func (sc *Subcommand) ByteSlice(assignmentVar *[]byte, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// Duration adds a new time.Duration flag. +// Input format is described in time.ParseDuration(). +// Example values: 1h, 1h50m, 32s +func (sc *Subcommand) Duration(assignmentVar *time.Duration, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// DurationSlice adds a new time.Duration flag. +// Input format is described in time.ParseDuration(). +// Example values: 1h, 1h50m, 32s +// Specify the flag multiple times to fill the slice. +func (sc *Subcommand) DurationSlice(assignmentVar *[]time.Duration, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// Float32 adds a new float32 flag. +func (sc *Subcommand) Float32(assignmentVar *float32, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// Float32Slice adds a new float32 flag. +// Specify the flag multiple times to fill the slice. +func (sc *Subcommand) Float32Slice(assignmentVar *[]float32, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// Float64 adds a new float64 flag. +func (sc *Subcommand) Float64(assignmentVar *float64, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// Float64Slice adds a new float64 flag. +// Specify the flag multiple times to fill the slice. +func (sc *Subcommand) Float64Slice(assignmentVar *[]float64, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// Int adds a new int flag +func (sc *Subcommand) Int(assignmentVar *int, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// IntSlice adds a new int slice flag. +// Specify the flag multiple times to fill the slice. +func (sc *Subcommand) IntSlice(assignmentVar *[]int, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// UInt adds a new uint flag +func (sc *Subcommand) UInt(assignmentVar *uint, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// UIntSlice adds a new uint slice flag. +// Specify the flag multiple times to fill the slice. +func (sc *Subcommand) UIntSlice(assignmentVar *[]uint, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// UInt64 adds a new uint64 flag +func (sc *Subcommand) UInt64(assignmentVar *uint64, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// UInt64Slice adds a new uint64 slice flag. +// Specify the flag multiple times to fill the slice. +func (sc *Subcommand) UInt64Slice(assignmentVar *[]uint64, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// UInt32 adds a new uint32 flag +func (sc *Subcommand) UInt32(assignmentVar *uint32, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// UInt32Slice adds a new uint32 slice flag. +// Specify the flag multiple times to fill the slice. +func (sc *Subcommand) UInt32Slice(assignmentVar *[]uint32, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// UInt16 adds a new uint16 flag +func (sc *Subcommand) UInt16(assignmentVar *uint16, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// UInt16Slice adds a new uint16 slice flag. +// Specify the flag multiple times to fill the slice. +func (sc *Subcommand) UInt16Slice(assignmentVar *[]uint16, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// UInt8 adds a new uint8 flag +func (sc *Subcommand) UInt8(assignmentVar *uint8, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// UInt8Slice adds a new uint8 slice flag. +// Specify the flag multiple times to fill the slice. +func (sc *Subcommand) UInt8Slice(assignmentVar *[]uint8, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// Int64 adds a new int64 flag. +func (sc *Subcommand) Int64(assignmentVar *int64, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// Int64Slice adds a new int64 slice flag. +// Specify the flag multiple times to fill the slice. +func (sc *Subcommand) Int64Slice(assignmentVar *[]int64, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// Int32 adds a new int32 flag +func (sc *Subcommand) Int32(assignmentVar *int32, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// Int32Slice adds a new int32 slice flag. +// Specify the flag multiple times to fill the slice. +func (sc *Subcommand) Int32Slice(assignmentVar *[]int32, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// Int16 adds a new int16 flag +func (sc *Subcommand) Int16(assignmentVar *int16, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// Int16Slice adds a new int16 slice flag. +// Specify the flag multiple times to fill the slice. +func (sc *Subcommand) Int16Slice(assignmentVar *[]int16, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// Int8 adds a new int8 flag +func (sc *Subcommand) Int8(assignmentVar *int8, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// Int8Slice adds a new int8 slice flag. +// Specify the flag multiple times to fill the slice. +func (sc *Subcommand) Int8Slice(assignmentVar *[]int8, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// IP adds a new net.IP flag. +func (sc *Subcommand) IP(assignmentVar *net.IP, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// IPSlice adds a new int8 slice flag. +// Specify the flag multiple times to fill the slice. +func (sc *Subcommand) IPSlice(assignmentVar *[]net.IP, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// HardwareAddr adds a new net.HardwareAddr flag. +func (sc *Subcommand) HardwareAddr(assignmentVar *net.HardwareAddr, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// HardwareAddrSlice adds a new net.HardwareAddr slice flag. +// Specify the flag multiple times to fill the slice. +func (sc *Subcommand) HardwareAddrSlice(assignmentVar *[]net.HardwareAddr, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// IPMask adds a new net.IPMask flag. IPv4 Only. +func (sc *Subcommand) IPMask(assignmentVar *net.IPMask, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// IPMaskSlice adds a new net.HardwareAddr slice flag. IPv4 only. +// Specify the flag multiple times to fill the slice. +func (sc *Subcommand) IPMaskSlice(assignmentVar *[]net.IPMask, shortName string, longName string, description string) { + sc.add(assignmentVar, shortName, longName, description) +} + +// AddPositionalValue adds a positional value to the subcommand. the +// relativePosition starts at 1 and is relative to the subcommand it belongs to +func (sc *Subcommand) AddPositionalValue(assignmentVar *string, name string, relativePosition int, required bool, description string) { + + // ensure no other positionals are at this depth + for _, other := range sc.PositionalFlags { + if relativePosition == other.Position { + log.Panicln("Unable to add positional value because one already exists at position: " + strconv.Itoa(relativePosition)) + } + } + + // ensure no subcommands at this depth + for _, other := range sc.Subcommands { + if relativePosition == other.Position { + log.Panicln("Unable to add positional value a subcommand already exists at position: " + strconv.Itoa(relativePosition)) + } + } + + newPositionalValue := PositionalValue{ + Name: name, + Position: relativePosition, + AssignmentVar: assignmentVar, + Required: required, + Description: description, + } + sc.PositionalFlags = append(sc.PositionalFlags, &newPositionalValue) +} + +// SetValueForKey sets the value for the specified key. If setting a bool +// value, then send "true" or "false" as strings. The returned bool indicates +// that a value was set. +func (sc *Subcommand) SetValueForKey(key string, value string) (bool, error) { + + // debugPrint("Looking to set key", key, "to value", value) + // check for and assign flags that match the key + for _, f := range sc.Flags { + // debugPrint("Evaluating string flag", f.ShortName, "==", key, "||", f.LongName, "==", key) + if f.ShortName == key || f.LongName == key { + // debugPrint("Setting string value for", key, "to", value) + f.identifyAndAssignValue(value) + return true, nil + } + } + + // debugPrint(sc.Name, "was unable to find a key named", key, "to set to value", value) + return false, nil +} + +// ensureNoConflictWithBuiltinHelp ensures that the flags on this subcommand do +// not conflict with the builtin help flags (-h or --help). Exits the program +// if a conflict is found. +func (sc *Subcommand) ensureNoConflictWithBuiltinHelp() { + for _, f := range sc.Flags { + if f.LongName == helpFlagLongName { + sc.exitBecauseOfHelpFlagConflict(f.LongName) + } + if f.LongName == helpFlagShortName { + sc.exitBecauseOfHelpFlagConflict(f.LongName) + } + if f.ShortName == helpFlagLongName { + sc.exitBecauseOfHelpFlagConflict(f.ShortName) + } + if f.ShortName == helpFlagShortName { + sc.exitBecauseOfHelpFlagConflict(f.ShortName) + } + } +} + +// ensureNoConflictWithBuiltinVersion ensures that the flags on this subcommand do +// not conflict with the builtin version flag (--version). Exits the program +// if a conflict is found. +func (sc *Subcommand) ensureNoConflictWithBuiltinVersion() { + for _, f := range sc.Flags { + if f.LongName == versionFlagLongName { + sc.exitBecauseOfVersionFlagConflict(f.LongName) + } + if f.ShortName == versionFlagLongName { + sc.exitBecauseOfVersionFlagConflict(f.ShortName) + } + } +} + +// exitBecauseOfVersionFlagConflict exits the program with a message about how to prevent +// flags being defined from conflicting with the builtin flags. +func (sc *Subcommand) exitBecauseOfVersionFlagConflict(flagName string) { + fmt.Println(`Flag with name '` + flagName + `' conflicts with the internal --version flag in flaggy. + +You must either change the flag's name, or disable flaggy's internal version +flag with 'flaggy.DefaultParser.ShowVersionWithVersionFlag = false'. If you are using +a custom parser, you must instead set '.ShowVersionWithVersionFlag = false' on it.`) + exitOrPanic(1) +} + +// exitBecauseOfHelpFlagConflict exits the program with a message about how to prevent +// flags being defined from conflicting with the builtin flags. +func (sc *Subcommand) exitBecauseOfHelpFlagConflict(flagName string) { + fmt.Println(`Flag with name '` + flagName + `' conflicts with the internal --help or -h flag in flaggy. + +You must either change the flag's name, or disable flaggy's internal help +flag with 'flaggy.DefaultParser.ShowHelpWithHFlag = false'. If you are using +a custom parser, you must instead set '.ShowHelpWithHFlag = false' on it.`) + exitOrPanic(1) +}