mirror of
https://github.com/jesseduffield/lazygit.git
synced 2024-12-10 11:10:18 +02:00
d8a6f173c3
Signed-off-by: Glenn Vriesman <glenn.vriesman@gmail.com>
623 lines
17 KiB
Go
623 lines
17 KiB
Go
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())
|
|
}
|
|
}
|