253 lines
4.5 KiB
Go
253 lines
4.5 KiB
Go
package wand
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/iancoleman/strcase"
|
|
"io"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
func formatHelp(w io.Writer, doc doc) error {
|
|
var err error
|
|
println := func(a ...any) {
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
_, err = fmt.Fprintln(w, a...)
|
|
}
|
|
|
|
printf := func(f string, a ...any) {
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
_, err = fmt.Fprintf(w, f, a...)
|
|
}
|
|
|
|
printf(doc.fullCommand)
|
|
println()
|
|
if doc.hasImplementation || doc.synopsis.hasSubcommands {
|
|
println()
|
|
printf("Synopsis")
|
|
println()
|
|
}
|
|
|
|
if doc.hasImplementation {
|
|
println()
|
|
printf(doc.synopsis.command)
|
|
if doc.synopsis.hasOptions {
|
|
printf(" [options ...]")
|
|
}
|
|
|
|
for _, n := range doc.synopsis.arguments.names {
|
|
printf(" %s", n)
|
|
}
|
|
|
|
if doc.synopsis.arguments.variadic {
|
|
printf("...")
|
|
if doc.synopsis.arguments.minPositional > 0 {
|
|
printf(" min %d arguments", doc.synopsis.arguments.minPositional)
|
|
}
|
|
|
|
if doc.synopsis.arguments.maxPositional > 0 {
|
|
printf(" max %d arguments", doc.synopsis.arguments.maxPositional)
|
|
}
|
|
}
|
|
|
|
println()
|
|
}
|
|
|
|
if doc.synopsis.hasSubcommands {
|
|
if !doc.hasImplementation {
|
|
println()
|
|
}
|
|
|
|
printf("%s <subcommand> [options or args...]", doc.synopsis.command)
|
|
println()
|
|
println()
|
|
printf("(For the details about the available subcommands, see the according section below.)")
|
|
println()
|
|
}
|
|
|
|
if len(doc.description) > 0 {
|
|
println()
|
|
printf(paragraphs(doc.description))
|
|
println()
|
|
}
|
|
|
|
if len(doc.options) > 0 {
|
|
println()
|
|
printf("Options")
|
|
println()
|
|
println()
|
|
printf("[*]: accepts multiple instances of the same option")
|
|
println()
|
|
printf("[b]: booelan flag, true or false, or no argument means true")
|
|
println()
|
|
println()
|
|
|
|
var names []string
|
|
od := make(map[string]string)
|
|
for _, o := range doc.options {
|
|
ons := []string{fmt.Sprintf("--%s", o.name)}
|
|
for _, sn := range o.shortNames {
|
|
ons = append(ons, fmt.Sprintf("-%s", sn))
|
|
}
|
|
|
|
n := strings.Join(ons, ", ")
|
|
if o.acceptsMultiple {
|
|
n = fmt.Sprintf("%s [*]", n)
|
|
}
|
|
|
|
if o.isBool {
|
|
n = fmt.Sprintf("%s [b]", n)
|
|
}
|
|
|
|
names = append(names, n)
|
|
od[n] = o.description
|
|
}
|
|
|
|
sort.Strings(names)
|
|
|
|
var max int
|
|
for _, n := range names {
|
|
if len(n) > max {
|
|
max = len(n)
|
|
}
|
|
}
|
|
|
|
for i := range names {
|
|
pad := strings.Join(make([]string, max-len(names[i])+1), " ")
|
|
names[i] = fmt.Sprintf("%s%s", names[i], pad)
|
|
}
|
|
|
|
for _, n := range names {
|
|
printf(n)
|
|
if od[n] != "" {
|
|
printf(": %s", paragraphs(od[n]))
|
|
}
|
|
|
|
println()
|
|
}
|
|
}
|
|
|
|
if len(doc.subcommands) > 0 {
|
|
println()
|
|
printf("Subcommands")
|
|
println()
|
|
println()
|
|
|
|
var names []string
|
|
cd := make(map[string]string)
|
|
for _, sc := range doc.subcommands {
|
|
name := sc.name
|
|
if sc.isDefault {
|
|
name = fmt.Sprintf("%s (default)", name)
|
|
}
|
|
|
|
d := sc.description
|
|
if sc.isHelp {
|
|
d = fmt.Sprintf("Show this help. %s", d)
|
|
}
|
|
|
|
if sc.hasHelpSubcommand {
|
|
d = fmt.Sprintf("%s - For help, see: %s %s help", d, doc.name, sc.name)
|
|
} else if sc.hasHelpOption {
|
|
d = fmt.Sprintf("%s - For help, see: %s %s --help", d, doc.name, sc.name)
|
|
}
|
|
|
|
cd[name] = d
|
|
}
|
|
|
|
sort.Strings(names)
|
|
|
|
var max int
|
|
for _, n := range names {
|
|
if len(n) > max {
|
|
max = len(n)
|
|
}
|
|
}
|
|
|
|
for i := range names {
|
|
pad := strings.Join(make([]string, max-len(names[i])+1), " ")
|
|
names[i] = fmt.Sprintf("%s%s", names[i], pad)
|
|
}
|
|
|
|
for _, n := range names {
|
|
printf(n)
|
|
if cd[n] != "" {
|
|
printf(": %s", paragraphs(cd[n]))
|
|
}
|
|
|
|
println()
|
|
}
|
|
}
|
|
|
|
if len(doc.options) > 0 {
|
|
printf(paragraphs(envDocs))
|
|
println()
|
|
println()
|
|
o := doc.options[0]
|
|
printf("Example environment variable:")
|
|
println()
|
|
println()
|
|
printf(strcase.ToSnake(o.name))
|
|
printf("=")
|
|
if o.isBool {
|
|
printf("true")
|
|
} else {
|
|
printf("42")
|
|
}
|
|
|
|
println()
|
|
}
|
|
|
|
if len(doc.options) > 0 && len(doc.configFiles) > 0 {
|
|
printf(paragraphs(configDocs))
|
|
println()
|
|
println()
|
|
printf("Config files:")
|
|
println()
|
|
println()
|
|
for _, cf := range doc.configFiles {
|
|
if cf.fromOption {
|
|
printf("zero or more configuration files defined by the --config option")
|
|
println()
|
|
continue
|
|
}
|
|
|
|
if cf.fn != "" {
|
|
printf(cf.fn)
|
|
if cf.optional {
|
|
printf(" (optional)")
|
|
}
|
|
|
|
println()
|
|
continue
|
|
}
|
|
}
|
|
|
|
println()
|
|
o := doc.options[0]
|
|
printf("Example configuration entry:")
|
|
println()
|
|
println()
|
|
printf("# default for --")
|
|
printf(o.name)
|
|
println()
|
|
printf(strcase.ToSnake(o.name))
|
|
printf(" = ")
|
|
if o.isBool {
|
|
printf("true")
|
|
} else {
|
|
printf("42")
|
|
}
|
|
|
|
println()
|
|
}
|
|
|
|
return err
|
|
}
|