2022-11-15 00:35:22 +00:00
|
|
|
package xmain
|
|
|
|
|
|
|
|
|
|
import (
|
2022-11-17 06:33:57 +00:00
|
|
|
"bytes"
|
2022-11-15 00:35:22 +00:00
|
|
|
"fmt"
|
|
|
|
|
"io"
|
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
|
|
"github.com/spf13/pflag"
|
|
|
|
|
"oss.terrastruct.com/cmdlog"
|
|
|
|
|
"oss.terrastruct.com/xos"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type Opts struct {
|
2022-11-17 00:48:19 +00:00
|
|
|
Args []string
|
|
|
|
|
Flags *pflag.FlagSet
|
2022-11-15 00:35:22 +00:00
|
|
|
env *xos.Env
|
|
|
|
|
log *cmdlog.Logger
|
|
|
|
|
|
2022-11-17 06:33:57 +00:00
|
|
|
flagEnv map[string]string
|
2022-11-15 00:35:22 +00:00
|
|
|
}
|
|
|
|
|
|
2022-11-17 00:42:39 +00:00
|
|
|
func NewOpts(env *xos.Env, log *cmdlog.Logger, args []string) *Opts {
|
2022-11-15 00:35:22 +00:00
|
|
|
flags := pflag.NewFlagSet("", pflag.ContinueOnError)
|
|
|
|
|
flags.SortFlags = false
|
|
|
|
|
flags.Usage = func() {}
|
|
|
|
|
flags.SetOutput(io.Discard)
|
|
|
|
|
return &Opts{
|
2022-11-17 06:33:57 +00:00
|
|
|
Args: args,
|
|
|
|
|
Flags: flags,
|
|
|
|
|
env: env,
|
|
|
|
|
log: log,
|
|
|
|
|
flagEnv: make(map[string]string),
|
2022-11-15 00:35:22 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-17 06:33:57 +00:00
|
|
|
// Mostly copy pasted pasted from pflag.FlagUsagesWrapped
|
|
|
|
|
// with modifications for env var
|
|
|
|
|
func (o *Opts) Defaults() string {
|
|
|
|
|
buf := new(bytes.Buffer)
|
|
|
|
|
|
|
|
|
|
var lines []string
|
|
|
|
|
|
|
|
|
|
maxlen := 0
|
|
|
|
|
maxEnvLen := 0
|
|
|
|
|
o.Flags.VisitAll(func(flag *pflag.Flag) {
|
|
|
|
|
if flag.Hidden {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
line := ""
|
|
|
|
|
if flag.Shorthand != "" && flag.ShorthandDeprecated == "" {
|
|
|
|
|
line = fmt.Sprintf(" -%s, --%s", flag.Shorthand, flag.Name)
|
|
|
|
|
} else {
|
|
|
|
|
line = fmt.Sprintf(" --%s", flag.Name)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
varname, usage := pflag.UnquoteUsage(flag)
|
|
|
|
|
if varname != "" {
|
|
|
|
|
line += " " + varname
|
|
|
|
|
}
|
|
|
|
|
if flag.NoOptDefVal != "" {
|
|
|
|
|
switch flag.Value.Type() {
|
|
|
|
|
case "string":
|
|
|
|
|
line += fmt.Sprintf("[=\"%s\"]", flag.NoOptDefVal)
|
|
|
|
|
case "bool":
|
|
|
|
|
if flag.NoOptDefVal != "true" {
|
|
|
|
|
line += fmt.Sprintf("[=%s]", flag.NoOptDefVal)
|
|
|
|
|
}
|
|
|
|
|
case "count":
|
|
|
|
|
if flag.NoOptDefVal != "+1" {
|
|
|
|
|
line += fmt.Sprintf("[=%s]", flag.NoOptDefVal)
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
line += fmt.Sprintf("[=%s]", flag.NoOptDefVal)
|
2022-11-15 00:35:22 +00:00
|
|
|
}
|
|
|
|
|
}
|
2022-11-17 06:33:57 +00:00
|
|
|
|
|
|
|
|
line += "\x00"
|
|
|
|
|
|
|
|
|
|
if len(line) > maxlen {
|
|
|
|
|
maxlen = len(line)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if e, ok := o.flagEnv[flag.Name]; ok {
|
|
|
|
|
line += fmt.Sprintf("$%s", e)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
line += "\x01"
|
|
|
|
|
|
|
|
|
|
if len(line) > maxEnvLen {
|
|
|
|
|
maxEnvLen = len(line)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
line += usage
|
|
|
|
|
if flag.Value.Type() == "string" {
|
|
|
|
|
line += fmt.Sprintf(" (default %q)", flag.DefValue)
|
|
|
|
|
} else {
|
|
|
|
|
line += fmt.Sprintf(" (default %s)", flag.DefValue)
|
|
|
|
|
}
|
|
|
|
|
if len(flag.Deprecated) != 0 {
|
|
|
|
|
line += fmt.Sprintf(" (DEPRECATED: %s)", flag.Deprecated)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lines = append(lines, line)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
for _, line := range lines {
|
|
|
|
|
sidx1 := strings.Index(line, "\x00")
|
|
|
|
|
sidx2 := strings.Index(line, "\x01")
|
|
|
|
|
spacing1 := strings.Repeat(" ", maxlen-sidx1)
|
|
|
|
|
spacing2 := strings.Repeat(" ", (maxEnvLen-maxlen)-sidx2+sidx1)
|
|
|
|
|
fmt.Fprintln(buf, line[:sidx1], spacing1, line[sidx1+1:sidx2], spacing2, wrap(maxEnvLen+3, 0, line[sidx2+1:]))
|
2022-11-15 00:35:22 +00:00
|
|
|
}
|
|
|
|
|
|
2022-11-17 06:33:57 +00:00
|
|
|
return buf.String()
|
2022-11-15 00:35:22 +00:00
|
|
|
}
|
|
|
|
|
|
2022-11-17 06:33:57 +00:00
|
|
|
func (o *Opts) getEnv(flag, k string) string {
|
2022-11-17 00:55:12 +00:00
|
|
|
if k != "" {
|
2022-11-17 06:33:57 +00:00
|
|
|
o.flagEnv[flag] = k
|
2022-11-17 00:55:12 +00:00
|
|
|
return o.env.Getenv(k)
|
|
|
|
|
}
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-17 00:42:39 +00:00
|
|
|
func (o *Opts) Int64(envKey, flag, shortFlag string, defaultVal int64, usage string) (*int64, error) {
|
2022-11-17 06:33:57 +00:00
|
|
|
if env := o.getEnv(flag, envKey); env != "" {
|
2022-11-17 00:55:12 +00:00
|
|
|
envVal, err := strconv.ParseInt(env, 10, 64)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf(`invalid environment variable %s. Expected int64. Found "%v".`, envKey, envVal)
|
2022-11-15 00:35:22 +00:00
|
|
|
}
|
2022-11-17 00:55:12 +00:00
|
|
|
defaultVal = envVal
|
2022-11-15 00:35:22 +00:00
|
|
|
}
|
|
|
|
|
|
2022-11-17 00:48:19 +00:00
|
|
|
return o.Flags.Int64P(flag, shortFlag, defaultVal, usage), nil
|
2022-11-15 00:35:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (o *Opts) String(envKey, flag, shortFlag string, defaultVal, usage string) *string {
|
2022-11-17 06:33:57 +00:00
|
|
|
if env := o.getEnv(flag, envKey); env != "" {
|
2022-11-17 00:55:12 +00:00
|
|
|
defaultVal = env
|
2022-11-15 00:35:22 +00:00
|
|
|
}
|
|
|
|
|
|
2022-11-17 00:48:19 +00:00
|
|
|
return o.Flags.StringP(flag, shortFlag, defaultVal, usage)
|
2022-11-15 00:35:22 +00:00
|
|
|
}
|
|
|
|
|
|
2022-11-17 00:42:39 +00:00
|
|
|
func (o *Opts) Bool(envKey, flag, shortFlag string, defaultVal bool, usage string) (*bool, error) {
|
2022-11-17 06:33:57 +00:00
|
|
|
if env := o.getEnv(flag, envKey); env != "" {
|
2022-11-17 00:55:12 +00:00
|
|
|
if !boolyEnv(env) {
|
|
|
|
|
return nil, fmt.Errorf(`invalid environment variable %s. Expected bool. Found "%s".`, envKey, env)
|
|
|
|
|
}
|
|
|
|
|
if truthyEnv(env) {
|
|
|
|
|
defaultVal = true
|
|
|
|
|
} else {
|
|
|
|
|
defaultVal = false
|
2022-11-15 00:35:22 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-17 00:48:19 +00:00
|
|
|
return o.Flags.BoolP(flag, shortFlag, defaultVal, usage), nil
|
2022-11-15 00:35:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func boolyEnv(s string) bool {
|
|
|
|
|
return falseyEnv(s) || truthyEnv(s)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func falseyEnv(s string) bool {
|
2022-11-17 00:42:39 +00:00
|
|
|
return s == "0" || s == "false"
|
2022-11-15 00:35:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func truthyEnv(s string) bool {
|
2022-11-17 00:42:39 +00:00
|
|
|
return s == "1" || s == "true"
|
2022-11-15 00:35:22 +00:00
|
|
|
}
|