mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-12-01 14:47:38 +00:00
126 lines
2.6 KiB
Go
126 lines
2.6 KiB
Go
package flagutil
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// NewArray returns new Array with the given name and description.
|
|
func NewArray(name, description string) *Array {
|
|
description += "\nSupports `array` of values separated by comma" +
|
|
" or specified via multiple flags."
|
|
var a Array
|
|
flag.Var(&a, name, description)
|
|
return &a
|
|
}
|
|
|
|
// Array is a flag that holds an array of values.
|
|
//
|
|
// It may be set either by specifying multiple flags with the given name
|
|
// passed to NewArray or by joining flag values by comma.
|
|
//
|
|
// The following example sets equivalent flag array with two items (value1, value2):
|
|
//
|
|
// -foo=value1 -foo=value2
|
|
// -foo=value1,value2
|
|
//
|
|
// Flag values may be quoted. For instance, the following arg creates an array of ("a", "b, c") items:
|
|
//
|
|
// -foo='a,"b, c"'
|
|
//
|
|
type Array []string
|
|
|
|
// String implements flag.Value interface
|
|
func (a *Array) String() string {
|
|
aEscaped := make([]string, len(*a))
|
|
for i, v := range *a {
|
|
if strings.ContainsAny(v, `", `+"\n") {
|
|
v = fmt.Sprintf("%q", v)
|
|
}
|
|
aEscaped[i] = v
|
|
}
|
|
return strings.Join(aEscaped, ",")
|
|
}
|
|
|
|
// Set implements flag.Value interface
|
|
func (a *Array) Set(value string) error {
|
|
values := parseArrayValues(value)
|
|
*a = append(*a, values...)
|
|
return nil
|
|
}
|
|
|
|
func parseArrayValues(s string) []string {
|
|
if len(s) == 0 {
|
|
return nil
|
|
}
|
|
var values []string
|
|
for {
|
|
v, tail := getNextArrayValue(s)
|
|
values = append(values, v)
|
|
if len(tail) == 0 {
|
|
return values
|
|
}
|
|
if tail[0] == ',' {
|
|
tail = tail[1:]
|
|
}
|
|
s = tail
|
|
}
|
|
}
|
|
|
|
func getNextArrayValue(s string) (string, string) {
|
|
if len(s) == 0 {
|
|
return "", ""
|
|
}
|
|
if s[0] != '"' {
|
|
// Fast path - unquoted string
|
|
n := strings.IndexByte(s, ',')
|
|
if n < 0 {
|
|
// The last item
|
|
return s, ""
|
|
}
|
|
return s[:n], s[n:]
|
|
}
|
|
|
|
// Find the end of quoted string
|
|
end := 1
|
|
ss := s[1:]
|
|
for {
|
|
n := strings.IndexByte(ss, '"')
|
|
if n < 0 {
|
|
// Cannot find trailing quote. Return the whole string till the end.
|
|
return s, ""
|
|
}
|
|
end += n + 1
|
|
// Verify whether the trailing quote is escaped with backslash.
|
|
backslashes := 0
|
|
for n > backslashes && ss[n-backslashes-1] == '\\' {
|
|
backslashes++
|
|
}
|
|
if backslashes&1 == 0 {
|
|
// The trailing quote isn't escaped.
|
|
break
|
|
}
|
|
// The trailing quote is escaped. Continue searching for the next quote.
|
|
ss = ss[n+1:]
|
|
}
|
|
v := s[:end]
|
|
vUnquoted, err := strconv.Unquote(v)
|
|
if err == nil {
|
|
v = vUnquoted
|
|
}
|
|
return v, s[end:]
|
|
}
|
|
|
|
// GetOptionalArg returns optional arg under the given argIdx.
|
|
func (a *Array) GetOptionalArg(argIdx int) string {
|
|
x := *a
|
|
if argIdx >= len(x) {
|
|
if len(x) == 1 {
|
|
return x[0]
|
|
}
|
|
return ""
|
|
}
|
|
return x[argIdx]
|
|
}
|