Skip to content

Commit

Permalink
Move usage into own file
Browse files Browse the repository at this point in the history
  • Loading branch information
evilmarty committed Jun 8, 2024
1 parent d2f90fa commit d5cb661
Show file tree
Hide file tree
Showing 5 changed files with 327 additions and 52 deletions.
52 changes: 3 additions & 49 deletions command_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,53 +39,7 @@ func (cs CommandSet) Description() string {
return ""
}

func (cs CommandSet) Usage() string {
var b strings.Builder
subcommands := cs.subcommands()
inputs := cs.Inputs()
if s := cs.Description(); s != "" {
fmt.Fprintf(&b, "%s\n\n", s)
}
usage := []string{"[flags]"}
if len(cs.Entrypoint) > 0 {
usage = append([]string{cs.Entrypoint[0]}, usage...)
usage = append(usage, cs.Entrypoint[1:]...)
}
if s := cs.String(); s != "" {
usage = append(usage, s)
}
if len(subcommands) > 0 {
usage = append(usage, "<commands>")
}
if len(inputs) > 0 {
usage = append(usage, "[inputs]")
}
if len(usage) > 0 {
fmt.Fprintf(&b, "USAGE\n %s\n", strings.Join(usage, " "))
}
if len(subcommands) > 0 {
fmt.Fprintf(&b, "\nCOMMANDS\n")
for _, subcommand := range subcommands {
fmt.Fprintf(&b, " %-20s %s\n", subcommand.Name, subcommand.Description)
}
}
if len(inputs) > 0 {
fmt.Fprintf(&b, "\nINPUTS\n")
for _, input := range inputs {
fmt.Fprintf(&b, " -%-19s %s\n", input.Name, input.Description)
}
}
if mainFlagSet.NFlag() > 0 {
b.WriteString("\nFLAGS\n")
mainFlagSet.VisitAll(func(f *flag.Flag) {
fmt.Fprintf(&b, " -%-19s %s\n", f.Name, f.Usage)
})
}
b.WriteString("\n")
return b.String()
}

func (cs CommandSet) subcommands() []ConfigCommand {
func (cs CommandSet) Subcommands() []ConfigCommand {
for i := len(cs.Commands) - 1; i >= 0; {
command := cs.Commands[i]
return command.Commands
Expand Down Expand Up @@ -174,9 +128,9 @@ func (cs CommandSet) RenderScriptToTemp(data map[string]any) (string, error) {
}

func (cs CommandSet) ParseArgs(values *map[string]any) error {
fs := flag.NewFlagSet(cs.String(), cs.ErrorHandling)
fs := flag.NewFlagSet(cs.String(), flag.ContinueOnError)
fs.Usage = func() {
fmt.Fprintf(fs.Output(), cs.Usage())
// Don't do anything here. We just want the error.
}
for _, input := range cs.Inputs() {
fs.String(input.Name, input.DefaultValue, input.Description)
Expand Down
4 changes: 2 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ var (

func main() {
mainFlagSet.Usage = func() {
fmt.Fprintf(mainFlagSet.Output(), "Usage of ILC:\n")
mainFlagSet.PrintDefaults()
u := NewUsage(os.Args[0:1], "ILC", "")
fmt.Printf("%s", u.String())
os.Exit(0)
}
mainFlagSet.BoolFunc("version", "Displays the version", func(_ string) error {
Expand Down
13 changes: 12 additions & 1 deletion runner.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"flag"
"fmt"
"os"

Expand All @@ -23,14 +24,24 @@ type Runner struct {
Entrypoint []string
}

func (r *Runner) printUsage(cs CommandSet) {
entrypoint := append([]string{}, r.Entrypoint...)
entrypoint = append(entrypoint, cs.String())
u := NewUsage(entrypoint, "ILC", cs.Description()).ImportCommandSet(cs)
fmt.Fprint(r.Stderr, u.String())
os.Exit(0)
}

func (r *Runner) Run() error {
cs, err := NewCommandSet(r.Entrypoint, r.Config, r.Args)
if err != nil {
return fmt.Errorf("failed to select command: %v", err)
}
values := make(map[string]any)
cs.ParseEnv(&values, r.Env)
if err = cs.ParseArgs(&values); err != nil {
if err = cs.ParseArgs(&values); err == flag.ErrHelp {
r.printUsage(cs)
} else if err != nil {
return fmt.Errorf("failed parsing arguments: %v", err)
}
if err = cs.AskInputs(&values); err != nil {
Expand Down
122 changes: 122 additions & 0 deletions usage.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package main

import (
"flag"
"fmt"
"io"
"strings"
)

type Usage struct {
Title string
Description string
commandNames []string
commandDescs []string
inputNames []string
inputDescs []string
flagNames []string
flagDescs []string
Entrypoint []string
}

func (u Usage) printSection(b io.Writer, header, content string) {
content = strings.ReplaceAll(content, "\n", "\n ")
content = strings.TrimSuffix(content, " ")
fmt.Fprintf(b, "%s\n %s\n", header, content)
}

func (u Usage) printInstructions(b io.Writer, names, descs []string, header, prefix string) {
var s strings.Builder
col := 15
for _, name := range names {
col = max(col, len([]rune(name)))
}
col = col + 5
format := fmt.Sprintf("%%-%ds %%s\n", col)
for i, name := range names {
desc := descs[i]
name = fmt.Sprintf("%s%s", prefix, name)
fmt.Fprintf(&s, format, name, desc)
}
u.printSection(b, header, s.String())
}

func (u Usage) usage() string {
params := []string{}
if len(u.Entrypoint) > 0 {
params = append(params, u.Entrypoint[0])
}
if len(u.flagNames) > 0 {
params = append(params, "[flags]")
}
if len(u.Entrypoint) > 1 {
params = append(params, u.Entrypoint[1:]...)
} else {
params = append(params, "<config>")
}
if len(u.commandNames) > 0 {
params = append(params, "<commands>")
}
if len(u.inputNames) > 0 {
params = append(params, "[inputs]")
}
return strings.Join(params, " ")
}

func (u Usage) String() string {
var b strings.Builder
if s := u.Title; s != "" {
fmt.Fprintf(&b, "%s\n\n", s)
}
if s := u.Description; s != "" {
fmt.Fprintf(&b, "%s\n\n", s)
}
if s := u.usage(); s != "" {
u.printSection(&b, "USAGE", s)
b.WriteString("\n")
}
if len(u.commandNames) > 0 {
u.printInstructions(&b, u.commandNames, u.commandDescs, "COMMANDS", "")
}
if len(u.inputNames) > 0 {
u.printInstructions(&b, u.inputNames, u.inputDescs, "INPUTS", "-")
}
if len(u.flagNames) > 0 {
u.printInstructions(&b, u.flagNames, u.flagDescs, "FLAGS", "-")
}
b.WriteString("\n")
return b.String()
}

func (u Usage) ImportCommands(commands []ConfigCommand) Usage {
for _, command := range commands {
u.commandNames = append(u.commandNames, command.Name)
u.commandDescs = append(u.commandDescs, command.Description)
}
return u
}

func (u Usage) ImportInputs(inputs []ConfigInput) Usage {
for _, input := range inputs {
u.inputNames = append(u.inputNames, input.Name)
u.inputDescs = append(u.inputDescs, input.Description)
}
return u
}

func (u Usage) ImportCommandSet(cs CommandSet) Usage {
return u.ImportCommands(cs.Subcommands()).ImportInputs(cs.Inputs())
}

func NewUsage(entrypoint []string, title, desc string) Usage {
u := Usage{
Title: title,
Description: desc,
Entrypoint: entrypoint,
}
mainFlagSet.VisitAll(func(f *flag.Flag) {
u.flagNames = append(u.flagNames, f.Name)
u.flagDescs = append(u.flagDescs, f.Usage)
})
return u
}
Loading

0 comments on commit d5cb661

Please sign in to comment.