summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/hashicorp/go-sockaddr/cmd/sockaddr/command/autohelp.go
blob: 082c53e27cb72ce13d66492a953c0735a02c9e06 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package command

import (
	"flag"
	"fmt"
	"sort"
	"strings"

	wordwrap "github.com/mitchellh/go-wordwrap"
	"github.com/ryanuber/columnize"
)

// AutoHelp specifies the necessary methods required to have their help
// completely generated for them.
type AutoHelp interface {
	Usage() string
	Description() string
	InitOpts()
	VisitAllFlags(func(f *flag.Flag))
}

// MakeHelp generates a help string based on the capabilities of the Command
func MakeHelp(c AutoHelp) string {
	usageText := c.Usage()

	// If the length of Usage() is zero, then assume this is a hidden
	// command.
	if len(usageText) == 0 {
		return ""
	}

	descriptionText := wordwrap.WrapString(c.Description(), 60)
	descrLines := strings.Split(descriptionText, "\n")
	prefixedLines := make([]string, len(descrLines))
	for i := range descrLines {
		prefixedLines[i] = "  " + descrLines[i]
	}
	descriptionText = strings.Join(prefixedLines, "\n")

	c.InitOpts()
	flags := []*flag.Flag{}
	c.VisitAllFlags(func(f *flag.Flag) {
		flags = append(flags, f)
	})
	optionsText := OptionsHelpOutput(flags)

	var helpOutput string
	switch {
	case len(optionsText) == 0 && len(descriptionText) == 0:
		helpOutput = usageText
	case len(optionsText) == 0:
		helpOutput = fmt.Sprintf(`Usage: %s

%s`,
			usageText, descriptionText)
	case len(descriptionText) == 0 && len(optionsText) > 0:
		helpOutput = fmt.Sprintf(`Usage: %s

Options:

%s`,
			usageText, optionsText)
	default:
		helpOutput = fmt.Sprintf(`Usage: %s

%s

Options:

%s`,
			usageText, descriptionText, optionsText)
	}

	return strings.TrimSpace(helpOutput)
}

// ByOptName implements sort.Interface for flag.Flag based on the Name field.
type ByName []*flag.Flag

func (a ByName) Len() int      { return len(a) }
func (a ByName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByName) Less(i, j int) bool {
	// Bubble up single-char args to the top of the list
	switch {
	case len(a[i].Name) == 1 && len(a[j].Name) != 1:
		return true
	case len(a[i].Name) != 1 && len(a[j].Name) == 1:
		return false
	default:
		// Case-insensitive sort.  Use case as a tie breaker, however.
		a1 := strings.ToLower(a[i].Name)
		a2 := strings.ToLower(a[j].Name)
		if a1 == a2 {
			return a[i].Name < a[j].Name
		} else {
			return a1 < a2
		}
	}
}

// OptionsHelpOutput returns a string of formatted options
func OptionsHelpOutput(flags []*flag.Flag) string {
	sort.Sort(ByName(flags))

	var output []string
	for _, f := range flags {
		if len(f.Usage) == 0 {
			continue
		}

		output = append(output, fmt.Sprintf("-%s | %s", f.Name, f.Usage))
	}

	optionsOutput := columnize.Format(output, &columnize.Config{
		Delim:  "|",
		Glue:   "  ",
		Prefix: "  ",
		Empty:  "",
	})
	return optionsOutput
}