mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-12-20 23:46:23 +01:00
d5c180e680
It is better developing vmctl tool in VictoriaMetrics repository, so it could be released together with the rest of vmutils tools such as vmalert, vmagent, vmbackup, vmrestore and vmauth.
95 lines
2.4 KiB
Go
95 lines
2.4 KiB
Go
package cli
|
|
|
|
import (
|
|
"flag"
|
|
"strings"
|
|
)
|
|
|
|
type iterativeParser interface {
|
|
newFlagSet() (*flag.FlagSet, error)
|
|
useShortOptionHandling() bool
|
|
}
|
|
|
|
// To enable short-option handling (e.g., "-it" vs "-i -t") we have to
|
|
// iteratively catch parsing errors. This way we achieve LR parsing without
|
|
// transforming any arguments. Otherwise, there is no way we can discriminate
|
|
// combined short options from common arguments that should be left untouched.
|
|
// Pass `shellComplete` to continue parsing options on failure during shell
|
|
// completion when, the user-supplied options may be incomplete.
|
|
func parseIter(set *flag.FlagSet, ip iterativeParser, args []string, shellComplete bool) error {
|
|
for {
|
|
err := set.Parse(args)
|
|
if !ip.useShortOptionHandling() || err == nil {
|
|
if shellComplete {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
errStr := err.Error()
|
|
trimmed := strings.TrimPrefix(errStr, "flag provided but not defined: -")
|
|
if errStr == trimmed {
|
|
return err
|
|
}
|
|
|
|
// regenerate the initial args with the split short opts
|
|
argsWereSplit := false
|
|
for i, arg := range args {
|
|
// skip args that are not part of the error message
|
|
if name := strings.TrimLeft(arg, "-"); name != trimmed {
|
|
continue
|
|
}
|
|
|
|
// if we can't split, the error was accurate
|
|
shortOpts := splitShortOptions(set, arg)
|
|
if len(shortOpts) == 1 {
|
|
return err
|
|
}
|
|
|
|
// swap current argument with the split version
|
|
args = append(args[:i], append(shortOpts, args[i+1:]...)...)
|
|
argsWereSplit = true
|
|
break
|
|
}
|
|
|
|
// This should be an impossible to reach code path, but in case the arg
|
|
// splitting failed to happen, this will prevent infinite loops
|
|
if !argsWereSplit {
|
|
return err
|
|
}
|
|
|
|
// Since custom parsing failed, replace the flag set before retrying
|
|
newSet, err := ip.newFlagSet()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*set = *newSet
|
|
}
|
|
}
|
|
|
|
func splitShortOptions(set *flag.FlagSet, arg string) []string {
|
|
shortFlagsExist := func(s string) bool {
|
|
for _, c := range s[1:] {
|
|
if f := set.Lookup(string(c)); f == nil {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
if !isSplittable(arg) || !shortFlagsExist(arg) {
|
|
return []string{arg}
|
|
}
|
|
|
|
separated := make([]string, 0, len(arg)-1)
|
|
for _, flagChar := range arg[1:] {
|
|
separated = append(separated, "-"+string(flagChar))
|
|
}
|
|
|
|
return separated
|
|
}
|
|
|
|
func isSplittable(flagArg string) bool {
|
|
return strings.HasPrefix(flagArg, "-") && !strings.HasPrefix(flagArg, "--") && len(flagArg) > 2
|
|
}
|