d2/lib/xmain/opts.go

174 lines
3.8 KiB
Go
Raw Normal View History

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, UsageErrorf(`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, UsageErrorf(`invalid environment variable %s. Expected bool. Found "%s".`, envKey, env)
2022-11-17 00:55:12 +00:00
}
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
}