2025-08-24 04:46:54 +02:00
|
|
|
package wand
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"github.com/iancoleman/strcase"
|
|
|
|
|
"io"
|
|
|
|
|
"sort"
|
|
|
|
|
"strings"
|
|
|
|
|
)
|
|
|
|
|
|
2025-08-26 03:21:35 +02:00
|
|
|
func formatHelp(out io.Writer, doc doc) error {
|
|
|
|
|
printf, println, finish := printer(out)
|
|
|
|
|
printf(escapeTeletype(doc.fullCommand))
|
2025-08-24 04:46:54 +02:00
|
|
|
println()
|
|
|
|
|
if doc.hasImplementation || doc.synopsis.hasSubcommands {
|
|
|
|
|
println()
|
|
|
|
|
printf("Synopsis")
|
|
|
|
|
println()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if doc.hasImplementation {
|
|
|
|
|
println()
|
2025-08-26 03:21:35 +02:00
|
|
|
printf(escapeTeletype(doc.synopsis.command))
|
2025-08-24 04:46:54 +02:00
|
|
|
if doc.synopsis.hasOptions {
|
2025-08-26 03:21:35 +02:00
|
|
|
printf(" [options...]")
|
2025-08-24 04:46:54 +02:00
|
|
|
}
|
|
|
|
|
|
2025-08-26 03:21:35 +02:00
|
|
|
for i := range doc.synopsis.arguments.names {
|
|
|
|
|
printf(
|
|
|
|
|
" [%s %s]",
|
|
|
|
|
doc.synopsis.arguments.names[i],
|
|
|
|
|
doc.synopsis.arguments.types[i],
|
|
|
|
|
)
|
2025-08-24 04:46:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if doc.synopsis.arguments.variadic {
|
|
|
|
|
printf("...")
|
2025-08-26 03:21:35 +02:00
|
|
|
min := doc.synopsis.arguments.minPositional
|
|
|
|
|
max := doc.synopsis.arguments.maxPositional
|
|
|
|
|
switch {
|
|
|
|
|
case min > 0 && max > 0:
|
|
|
|
|
printf("\nmin %d and max %d total positional arguments", min, max)
|
|
|
|
|
case min > 0:
|
|
|
|
|
printf("\nmin %d total positional arguments", min)
|
|
|
|
|
case max > 0:
|
|
|
|
|
printf("\nmax %d total positional arguments", max)
|
2025-08-24 04:46:54 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
println()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if doc.synopsis.hasSubcommands {
|
2025-08-26 03:21:35 +02:00
|
|
|
println()
|
|
|
|
|
printf("%s <subcommand> [options or args...]", escapeTeletype(doc.synopsis.command))
|
2025-08-24 04:46:54 +02:00
|
|
|
println()
|
|
|
|
|
println()
|
2025-08-26 03:21:35 +02:00
|
|
|
printf("(For the details about the available subcommands, see the related section below.)")
|
2025-08-24 04:46:54 +02:00
|
|
|
println()
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-26 03:21:35 +02:00
|
|
|
if doc.description != "" {
|
2025-08-24 04:46:54 +02:00
|
|
|
println()
|
2025-08-26 03:21:35 +02:00
|
|
|
printf(escapeTeletype(paragraphs(doc.description)))
|
2025-08-24 04:46:54 +02:00
|
|
|
println()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(doc.options) > 0 {
|
|
|
|
|
println()
|
|
|
|
|
printf("Options")
|
2025-08-26 03:21:35 +02:00
|
|
|
if doc.hasBoolOptions || doc.hasListOptions {
|
|
|
|
|
println()
|
|
|
|
|
if doc.hasBoolOptions {
|
|
|
|
|
println()
|
|
|
|
|
printf("[b]: booelan flag, true or false, or no argument means true")
|
2025-08-24 04:46:54 +02:00
|
|
|
}
|
|
|
|
|
|
2025-08-26 03:21:35 +02:00
|
|
|
if doc.hasListOptions {
|
|
|
|
|
println()
|
|
|
|
|
printf("[*]: accepts multiple instances of the same option")
|
2025-08-24 04:46:54 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-26 03:21:35 +02:00
|
|
|
println()
|
|
|
|
|
println()
|
|
|
|
|
names, od := prepareOptions(doc.options)
|
2025-08-24 04:46:54 +02:00
|
|
|
|
|
|
|
|
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), " ")
|
2025-08-26 03:21:35 +02:00
|
|
|
names[i] = fmt.Sprintf("%s:%s", names[i], pad)
|
2025-08-24 04:46:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, n := range names {
|
|
|
|
|
printf(n)
|
|
|
|
|
if od[n] != "" {
|
2025-08-26 03:21:35 +02:00
|
|
|
printf(" %s", escapeTeletype(lines(od[n])))
|
2025-08-24 04:46:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 {
|
2025-08-26 03:21:35 +02:00
|
|
|
d = fmt.Sprintf("%s - For help, see: %s %s help", d, escapeTeletype(doc.name), sc.name)
|
2025-08-24 04:46:54 +02:00
|
|
|
} else if sc.hasHelpOption {
|
2025-08-26 03:21:35 +02:00
|
|
|
d = fmt.Sprintf("%s - For help, see: %s %s --help", d, escapeTeletype(doc.name), sc.name)
|
2025-08-24 04:46:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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] != "" {
|
2025-08-26 03:21:35 +02:00
|
|
|
printf(": %s", escapeTeletype(paragraphs(cd[n])))
|
2025-08-24 04:46:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
println()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-26 03:21:35 +02:00
|
|
|
if len(doc.options) > 0 && commandNameExpression.MatchString(doc.appName) {
|
|
|
|
|
println("Environment Variables")
|
|
|
|
|
println()
|
|
|
|
|
printf(escapeTeletype(paragraphs(envDocs)))
|
2025-08-24 04:46:54 +02:00
|
|
|
println()
|
|
|
|
|
println()
|
|
|
|
|
o := doc.options[0]
|
|
|
|
|
printf("Example environment variable:")
|
|
|
|
|
println()
|
|
|
|
|
println()
|
2025-08-26 03:21:35 +02:00
|
|
|
printf(strcase.ToSnake(fmt.Sprintf("%s-%s", doc.appName, o.name)))
|
2025-08-24 04:46:54 +02:00
|
|
|
printf("=")
|
|
|
|
|
if o.isBool {
|
|
|
|
|
printf("true")
|
|
|
|
|
} else {
|
|
|
|
|
printf("42")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
println()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(doc.options) > 0 && len(doc.configFiles) > 0 {
|
2025-08-26 03:21:35 +02:00
|
|
|
println("Configuration Files")
|
|
|
|
|
println()
|
|
|
|
|
printf(escapeTeletype(paragraphs(configDocs)))
|
2025-08-24 04:46:54 +02:00
|
|
|
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)
|
2025-08-26 03:21:35 +02:00
|
|
|
printf(":")
|
2025-08-24 04:46:54 +02:00
|
|
|
println()
|
|
|
|
|
printf(strcase.ToSnake(o.name))
|
|
|
|
|
printf(" = ")
|
|
|
|
|
if o.isBool {
|
|
|
|
|
printf("true")
|
|
|
|
|
} else {
|
|
|
|
|
printf("42")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
println()
|
2025-08-26 03:21:35 +02:00
|
|
|
println()
|
|
|
|
|
printf("Example for discarding an inherited entry:")
|
|
|
|
|
println()
|
|
|
|
|
println()
|
|
|
|
|
printf("# discarding an inherited entry:")
|
|
|
|
|
println()
|
|
|
|
|
printf(strcase.ToSnake(o.name))
|
|
|
|
|
println()
|
2025-08-24 04:46:54 +02:00
|
|
|
}
|
|
|
|
|
|
2025-08-26 03:21:35 +02:00
|
|
|
return finish()
|
2025-08-24 04:46:54 +02:00
|
|
|
}
|