2022-11-16 10:33:57PM

This commit is contained in:
Alexander Wang 2022-11-16 22:33:57 -08:00
parent 7d9ace3ae4
commit 052b461b03
No known key found for this signature in database
GPG key ID: D89FA31966BDBECE
3 changed files with 134 additions and 24 deletions

View file

@ -28,7 +28,7 @@ Subcommands:
%[1]s layout [layout name] - Display long help for a particular layout engine
See more docs and the source code at https://oss.terrastruct.com/d2
`, ms.Name, ms.Opts.Help())
`, ms.Name, ms.Opts.Defaults())
}
func layoutHelp(ctx context.Context, ms *xmain.State) error {

45
lib/xmain/flag_helpers.go Normal file
View file

@ -0,0 +1,45 @@
// flag_helpers.go are private functions from pflag/flag.go
package xmain
import "strings"
func wrap(i, w int, s string) string {
if w == 0 {
return strings.Replace(s, "\n", "\n"+strings.Repeat(" ", i), -1)
}
wrap := w - i
var r, l string
if wrap < 24 {
i = 16
wrap = w - i
r += "\n" + strings.Repeat(" ", i)
}
if wrap < 24 {
return strings.Replace(s, "\n", r, -1)
}
slop := 5
wrap = wrap - slop
l, s = wrapN(wrap, slop, s)
r = r + strings.Replace(l, "\n", "\n"+strings.Repeat(" ", i), -1)
for s != "" {
var t string
t, s = wrapN(wrap, slop, s)
r = r + "\n" + strings.Repeat(" ", i) + strings.Replace(t, "\n", "\n"+strings.Repeat(" ", i), -1)
}
return r
}
func wrapN(i, slop int, s string) (string, string) {
if i+slop > len(s) {
return s, ""
}
w := strings.LastIndexAny(s[:i], " \t\n")
if w <= 0 {
return s, ""
}
nlPos := strings.LastIndex(s[:i], "\n")
if nlPos > 0 && nlPos < w {
return s[:nlPos], s[nlPos+1:]
}
return s[:w], s[w+1:]
}

View file

@ -1,6 +1,7 @@
package xmain
import (
"bytes"
"fmt"
"io"
"strconv"
@ -17,7 +18,7 @@ type Opts struct {
env *xos.Env
log *cmdlog.Logger
registeredEnvs []string
flagEnv map[string]string
}
func NewOpts(env *xos.Env, log *cmdlog.Logger, args []string) *Opts {
@ -26,42 +27,106 @@ func NewOpts(env *xos.Env, log *cmdlog.Logger, args []string) *Opts {
flags.Usage = func() {}
flags.SetOutput(io.Discard)
return &Opts{
Args: args,
Flags: flags,
env: env,
log: log,
Args: args,
Flags: flags,
env: env,
log: log,
flagEnv: make(map[string]string),
}
}
func (o *Opts) Help() string {
b := &strings.Builder{}
o.Flags.SetOutput(b)
o.Flags.PrintDefaults()
// Mostly copy pasted pasted from pflag.FlagUsagesWrapped
// with modifications for env var
func (o *Opts) Defaults() string {
buf := new(bytes.Buffer)
if len(o.registeredEnvs) > 0 {
b.WriteString("\nYou may persistently set the following as environment variables (flags take precedent):\n")
for i, e := range o.registeredEnvs {
s := fmt.Sprintf("- $%s", e)
if i != len(o.registeredEnvs)-1 {
s += "\n"
}
b.WriteString(s)
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)
}
}
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:]))
}
return b.String()
return buf.String()
}
func (o *Opts) getEnv(k string) string {
func (o *Opts) getEnv(flag, k string) string {
if k != "" {
o.registeredEnvs = append(o.registeredEnvs, k)
o.flagEnv[flag] = k
return o.env.Getenv(k)
}
return ""
}
func (o *Opts) Int64(envKey, flag, shortFlag string, defaultVal int64, usage string) (*int64, error) {
if env := o.getEnv(envKey); env != "" {
if env := o.getEnv(flag, envKey); env != "" {
envVal, err := strconv.ParseInt(env, 10, 64)
if err != nil {
return nil, fmt.Errorf(`invalid environment variable %s. Expected int64. Found "%v".`, envKey, envVal)
@ -73,7 +138,7 @@ func (o *Opts) Int64(envKey, flag, shortFlag string, defaultVal int64, usage str
}
func (o *Opts) String(envKey, flag, shortFlag string, defaultVal, usage string) *string {
if env := o.getEnv(envKey); env != "" {
if env := o.getEnv(flag, envKey); env != "" {
defaultVal = env
}
@ -81,7 +146,7 @@ func (o *Opts) String(envKey, flag, shortFlag string, defaultVal, usage string)
}
func (o *Opts) Bool(envKey, flag, shortFlag string, defaultVal bool, usage string) (*bool, error) {
if env := o.getEnv(envKey); env != "" {
if env := o.getEnv(flag, envKey); env != "" {
if !boolyEnv(env) {
return nil, fmt.Errorf(`invalid environment variable %s. Expected bool. Found "%s".`, envKey, env)
}