github.com/thanos-io/thanos@v0.32.5/pkg/extkingpin/app.go (about) 1 // Copyright (c) The Thanos Authors. 2 // Licensed under the Apache License 2.0. 3 4 package extkingpin 5 6 import ( 7 "fmt" 8 "os" 9 "sort" 10 "text/template" 11 12 "github.com/go-kit/log" 13 "github.com/oklog/run" 14 "github.com/opentracing/opentracing-go" 15 "github.com/pkg/errors" 16 "github.com/prometheus/client_golang/prometheus" 17 "gopkg.in/alecthomas/kingpin.v2" 18 ) 19 20 const UsageTemplate = `{{define "FormatCommand"}}\ 21 {{if .FlagSummary}} {{.FlagSummary}}{{end}}\ 22 {{range .Args}} {{if not .Required}}[{{end}}<{{.Name}}>{{if .Value|IsCumulative}}...{{end}}{{if not .Required}}]{{end}}{{end}}\ 23 {{end}}\ 24 25 {{define "FormatCommands"}}\ 26 {{range .FlattenedCommands}}\ 27 {{if not .Hidden}}\ 28 {{.FullCommand}}{{if .Default}}*{{end}}{{template "FormatCommand" .}} 29 {{.Help|Wrap 4}} 30 {{end}}\ 31 {{end}}\ 32 {{end}}\ 33 34 {{define "FormatUsage"}}\ 35 {{template "FormatCommand" .}}{{if .Commands}} <command> [<args> ...]{{end}} 36 {{if .Help}} 37 {{.Help|Wrap 0}}\ 38 {{end}}\ 39 40 {{end}}\ 41 42 {{if .Context.SelectedCommand}}\ 43 usage: {{.App.Name}} {{.Context.SelectedCommand}}{{template "FormatUsage" .Context.SelectedCommand}} 44 {{else}}\ 45 usage: {{.App.Name}}{{template "FormatUsage" .App}} 46 {{end}}\ 47 {{if .Context.Flags}}\ 48 Flags: 49 {{alphabeticalSort .Context.Flags|FlagsToTwoColumns|FormatTwoColumns}} 50 {{end}}\ 51 {{if .Context.Args}}\ 52 Args: 53 {{.Context.Args|ArgsToTwoColumns|FormatTwoColumns}} 54 {{end}}\ 55 {{if .Context.SelectedCommand}}\ 56 {{if len .Context.SelectedCommand.Commands}}\ 57 Subcommands: 58 {{template "FormatCommands" .Context.SelectedCommand}} 59 {{end}}\ 60 {{else if .App.Commands}}\ 61 Commands: 62 {{template "FormatCommands" .App}} 63 {{end}}\ 64 ` 65 66 type FlagClause interface { 67 Flag(name, help string) *kingpin.FlagClause 68 } 69 70 // TODO(bwplotka): Consider some extkingpin package that will not depend on those. Needed: Generics! 71 type SetupFunc func(*run.Group, log.Logger, *prometheus.Registry, opentracing.Tracer, <-chan struct{}, bool) error 72 73 type AppClause interface { 74 FlagClause 75 Command(cmd string, help string) AppClause 76 Flags() []*kingpin.FlagModel 77 Setup(s SetupFunc) 78 } 79 80 // App is a wrapper around kingping.Application for easier use. 81 type App struct { 82 FlagClause 83 app *kingpin.Application 84 setups map[string]SetupFunc 85 } 86 87 // NewApp returns new App. 88 func NewApp(app *kingpin.Application) *App { 89 app.HelpFlag.Short('h') 90 app.UsageTemplate(UsageTemplate) 91 app.UsageFuncs(template.FuncMap{ 92 "alphabeticalSort": func(data []*kingpin.FlagModel) []*kingpin.FlagModel { 93 sort.Slice(data, func(i, j int) bool { return data[i].Name < data[j].Name }) 94 return data 95 }, 96 }) 97 return &App{ 98 app: app, 99 FlagClause: app, 100 setups: map[string]SetupFunc{}, 101 } 102 } 103 104 func (a *App) Parse() (cmd string, setup SetupFunc) { 105 cmd, err := a.app.Parse(os.Args[1:]) 106 if err != nil { 107 a.app.Usage(os.Args[1:]) 108 fmt.Fprintln(os.Stderr, errors.Wrapf(err, "error parsing commandline arguments: %v", os.Args)) 109 os.Exit(2) 110 } 111 return cmd, a.setups[cmd] 112 } 113 114 func (a *App) Command(cmd, help string) AppClause { 115 c := a.app.Command(cmd, help) 116 return &appClause{ 117 c: c, 118 FlagClause: c, 119 setups: a.setups, 120 prefix: cmd, 121 } 122 } 123 124 type appClause struct { 125 c *kingpin.CmdClause 126 127 FlagClause 128 setups map[string]SetupFunc 129 prefix string 130 } 131 132 func (a *appClause) Command(cmd, help string) AppClause { 133 c := a.c.Command(cmd, help) 134 return &appClause{ 135 c: c, 136 FlagClause: c, 137 setups: a.setups, 138 prefix: a.prefix + " " + cmd, 139 } 140 } 141 142 func (a *appClause) Setup(s SetupFunc) { 143 a.setups[a.prefix] = s 144 } 145 146 func (a *appClause) Flags() []*kingpin.FlagModel { 147 return a.c.Model().Flags 148 }