package wand import ( "fmt" "github.com/iancoleman/strcase" "io" "strings" "time" ) func formatManCommand(printf func(string, ...any), println func(...any), doc doc) { println(".SH Synopsis") printf(".B %s", escapeRoff(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], ) } min := doc.synopsis.arguments.minPositional max := doc.synopsis.arguments.maxPositional if doc.synopsis.arguments.variadic { println("...") if min > 0 || max > 0 { println(".PP") } 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 { if min > 0 || max > 0 { println(".PP") } for i, sc := range doc.subcommands { if i > 0 { println(".br") } printf("%s %s\n", escapeRoff(doc.name), sc.name) } } if doc.description != "" { println(".SH Description") println(manParagraphs(escapeRoff(doc.description))) } if len(doc.options) > 0 { println(".SH Options") if doc.hasBoolOptions || doc.hasListOptions { println(".PP") } if doc.hasBoolOptions { println(".B [b]:") println("booelan flag, true or false, or no argument means true") } if doc.hasListOptions { if doc.hasBoolOptions { println(".br") } println(".B [*]:") println("accepts multiple instances of the same option") } names, descriptions := prepareOptions(doc.options) for _, n := range names { println(".TP") printf(".B %s\n", escapeRoff(n)) if descriptions[n] != "" { println(manLines(escapeRoff(descriptions[n]))) } } } if len(doc.options) > 0 && commandNameExpression.MatchString(doc.appName) { println(".SH Environment Variables") println(manParagraphs(escapeRoff(envDocs))) println(".PP Example environment variable:") o := doc.options[0] println(".TP") printf(strcase.ToSnake(fmt.Sprintf("%s-%s", doc.appName, o.name))) printf("=") if o.isBool { printf("true") } else { printf("42") } println() } if len(doc.options) > 0 && len(doc.configFiles) > 0 { println(".SH Configuration Files") println(manParagraphs(escapeRoff(configDocs))) println(".PP Config files:") for i, cf := range doc.configFiles { if i > 0 { println(".br") } if cf.fromOption { println(escapeRoff("zero or more configuration files defined by the --config option")) continue } if cf.fn != "" { printf(escapeRoff(cf.fn)) if cf.optional { printf(" (optional)") } println() continue } } println(".PP Example configuration entry:") println(".PP") o := doc.options[0] printf(escapeRoff(fmt.Sprintf("# default for --%s:\n", o.name))) println(".br") printf(escapeRoff(strcase.ToSnake(o.name))) printf(" = ") if o.isBool { printf("true") } else { printf("42") } println() println(".PP Example for discarding an inherited entry:") println(".PP") println("# discarding an inherited entry:") println(".br") println(escapeRoff(strcase.ToSnake(o.name))) } } func formatManMultiCommand(out io.Writer, doc doc) error { printf, println, finish := printer(out) printf(".TH %s 1 %s \"%s\"\n", escapeRoff(doc.appName), escapeRoff(doc.date.Format(time.DateOnly)), escapeRoff(doc.appName)) printf(".SH Name\n%s\n", escapeRoff(doc.appName)) println(".SH Provides several commands:") println(".PP") allCommands := allCommands(doc) for i, c := range allCommands { if i > 0 { println(".br") } println(escapeRoff(c.fullCommand)) } for _, c := range allCommands { printf(".SH %s\n", escapeRoff(strings.ToUpper(c.fullCommand))) formatManCommand(printf, println, c) } return finish() } func formatManSingleCommand(out io.Writer, doc doc) error { printf, println, finish := printer(out) printf(".TH %s 1 %s \"%s\"\n", escapeRoff(doc.appName), escapeRoff(doc.date.Format(time.DateOnly)), escapeRoff(doc.appName)) printf(".SH Name\n%s\n", escapeRoff(doc.appName)) formatManCommand(printf, println, doc) return finish() } func formatMan(out io.Writer, doc doc) error { var hasSubcommands bool for _, sc := range doc.subcommands { if !sc.isHelp && !sc.isVersion { continue } hasSubcommands = true break } if hasSubcommands { return formatManMultiCommand(out, doc) } return formatManSingleCommand(out, doc) }