wand/formatmarkdown.go
2025-08-26 03:21:35 +02:00

202 lines
4.4 KiB
Go

package wand
import (
"fmt"
"github.com/iancoleman/strcase"
"io"
"strings"
)
func header(level int) string {
s := make([]string, level+2)
return strings.Join(s, "#")
}
func formatMarkdownCommand(printf func(string, ...any), println func(...any), doc doc, level int) {
printf("%s Synopsis\n\n", header(level))
println("```")
printf(doc.synopsis.command)
if doc.synopsis.hasOptions {
printf(" [options...]")
}
for i := range doc.synopsis.arguments.names {
printf(
" [%s %s]",
doc.synopsis.arguments.names[i],
doc.synopsis.arguments.types[i],
)
}
if doc.synopsis.arguments.variadic {
printf("...")
}
println()
println("```")
min := doc.synopsis.arguments.minPositional
max := doc.synopsis.arguments.maxPositional
if doc.synopsis.arguments.variadic {
if min > 0 || max > 0 {
println()
}
switch {
case min > 0 && max > 0:
printf("min %d and max %d total positional arguments\n", min, max)
case min > 0:
printf("min %d total positional arguments\n", min)
case max > 0:
printf("max %d total positional arguments\n", max)
}
}
if doc.synopsis.hasSubcommands {
println()
println("```")
for _, sc := range doc.subcommands {
printf("%s %s\n", escapeMD(doc.name), sc.name)
}
println("```")
}
if doc.description != "" {
printf("\n%s Description\n\n", header(level))
println(escapeMD(paragraphs(doc.description)))
}
if len(doc.options) > 0 {
printf("\n%s Options\n\n", header(level))
if doc.hasBoolOptions {
printf("- [b]: booelan flag, true or false, or no argument means true\n")
}
if doc.hasListOptions {
printf("- [*]: accepts multiple instances of the same option\n")
}
if doc.hasBoolOptions || doc.hasListOptions {
println()
}
names, descriptions := prepareOptions(doc.options)
for _, n := range names {
printf("- **%s**: %s\n", escapeMD(n), escapeMD(lines(descriptions[n])))
}
}
if len(doc.options) > 0 && commandNameExpression.MatchString(doc.appName) {
printf("\n.%s Environment Variables\n\n", header(level))
println(escapeMD(paragraphs(envDocs)))
println()
println("Example environment variable:")
println()
o := doc.options[0]
println("```")
printf(strcase.ToSnake(fmt.Sprintf("%s-%s", doc.appName, o.name)))
printf("=")
if o.isBool {
printf("true")
} else {
printf("42")
}
println()
println("```")
}
if len(doc.options) > 0 && len(doc.configFiles) > 0 {
printf("\n.Configuration Files\n\n")
println(escapeMD(paragraphs(configDocs)))
println()
println("Config files:")
println()
for _, cf := range doc.configFiles {
if cf.fromOption {
println("- zero or more configuration files defined by the --config option\n")
continue
}
if cf.fn != "" {
printf("- %s", cf.fn)
if cf.optional {
printf(" (optional)")
}
println()
continue
}
}
println()
println("Example configuration entry:")
println()
o := doc.options[0]
println("```")
printf("# default for --%s:\n", o.name)
printf(strcase.ToSnake(o.name))
printf(" = ")
if o.isBool {
printf("true")
} else {
printf("42")
}
println()
println("```")
println()
println("Example for discarding an inherited entry:")
println()
println("```")
println("# discarding an inherited entry:")
println(strcase.ToSnake(o.name))
println("```")
}
}
func formatMarkdownMultiCommand(out io.Writer, doc doc, level int) error {
printf, println, finish := printer(out)
printf("%s %s\n\n", header(level), escapeMD(doc.appName))
println("Provides several commands:")
println()
allCommands := allCommands(doc)
for _, c := range allCommands {
printf("- %s\n", escapeMD(c.fullCommand))
}
println()
for _, c := range allCommands {
printf("%s %s\n\n", header(level+1), escapeMD(c.fullCommand))
formatMarkdownCommand(printf, println, c, level+2)
}
return finish()
}
func formatMarkdownSingleCommand(out io.Writer, doc doc, level int) error {
printf, println, finish := printer(out)
printf("%s %s\n\n", header(level), doc.appName)
formatMarkdownCommand(printf, println, doc, level+1)
return finish()
}
func formatMarkdown(out io.Writer, doc doc, level int) error {
var hasSubcommands bool
for _, sc := range doc.subcommands {
if !sc.isHelp && !sc.isVersion {
continue
}
hasSubcommands = true
break
}
if hasSubcommands {
return formatMarkdownMultiCommand(out, doc, level)
}
return formatMarkdownSingleCommand(out, doc, level)
}