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 [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 }