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  }