go.etcd.io/etcd@v3.3.27+incompatible/functional/runner/help.go (about)

     1  // Copyright 2015 The etcd Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // copied from https://github.com/rkt/rkt/blob/master/rkt/help.go
    16  
    17  package runner
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"io"
    23  	"os"
    24  	"strings"
    25  	"text/tabwriter"
    26  	"text/template"
    27  
    28  	"github.com/coreos/etcd/version"
    29  
    30  	"github.com/spf13/cobra"
    31  	"github.com/spf13/pflag"
    32  )
    33  
    34  var (
    35  	commandUsageTemplate *template.Template
    36  	templFuncs           = template.FuncMap{
    37  		"descToLines": func(s string) []string {
    38  			// trim leading/trailing whitespace and split into slice of lines
    39  			return strings.Split(strings.Trim(s, "\n\t "), "\n")
    40  		},
    41  		"cmdName": func(cmd *cobra.Command, startCmd *cobra.Command) string {
    42  			parts := []string{cmd.Name()}
    43  			for cmd.HasParent() && cmd.Parent().Name() != startCmd.Name() {
    44  				cmd = cmd.Parent()
    45  				parts = append([]string{cmd.Name()}, parts...)
    46  			}
    47  			return strings.Join(parts, " ")
    48  		},
    49  	}
    50  )
    51  
    52  func init() {
    53  	commandUsage := `
    54  {{ $cmd := .Cmd }}\
    55  {{ $cmdname := cmdName .Cmd .Cmd.Root }}\
    56  NAME:
    57  {{ if not .Cmd.HasParent }}\
    58  {{printf "\t%s - %s" .Cmd.Name .Cmd.Short}}
    59  {{else}}\
    60  {{printf "\t%s - %s" $cmdname .Cmd.Short}}
    61  {{end}}\
    62  
    63  USAGE:
    64  {{printf "\t%s" .Cmd.UseLine}}
    65  {{ if not .Cmd.HasParent }}\
    66  
    67  VERSION:
    68  {{printf "\t%s" .Version}}
    69  {{end}}\
    70  {{if .Cmd.HasSubCommands}}\
    71  
    72  API VERSION:
    73  {{printf "\t%s" .APIVersion}}
    74  {{end}}\
    75  {{if .Cmd.HasSubCommands}}\
    76  
    77  
    78  COMMANDS:
    79  {{range .SubCommands}}\
    80  {{ $cmdname := cmdName . $cmd }}\
    81  {{ if .Runnable }}\
    82  {{printf "\t%s\t%s" $cmdname .Short}}
    83  {{end}}\
    84  {{end}}\
    85  {{end}}\
    86  {{ if .Cmd.Long }}\
    87  
    88  DESCRIPTION:
    89  {{range $line := descToLines .Cmd.Long}}{{printf "\t%s" $line}}
    90  {{end}}\
    91  {{end}}\
    92  {{if .Cmd.HasLocalFlags}}\
    93  
    94  OPTIONS:
    95  {{.LocalFlags}}\
    96  {{end}}\
    97  {{if .Cmd.HasInheritedFlags}}\
    98  
    99  GLOBAL OPTIONS:
   100  {{.GlobalFlags}}\
   101  {{end}}
   102  `[1:]
   103  
   104  	commandUsageTemplate = template.Must(template.New("command_usage").Funcs(templFuncs).Parse(strings.Replace(commandUsage, "\\\n", "", -1)))
   105  }
   106  
   107  func etcdFlagUsages(flagSet *pflag.FlagSet) string {
   108  	x := new(bytes.Buffer)
   109  
   110  	flagSet.VisitAll(func(flag *pflag.Flag) {
   111  		if len(flag.Deprecated) > 0 {
   112  			return
   113  		}
   114  		var format string
   115  		if len(flag.Shorthand) > 0 {
   116  			format = "  -%s, --%s"
   117  		} else {
   118  			format = "   %s   --%s"
   119  		}
   120  		if len(flag.NoOptDefVal) > 0 {
   121  			format = format + "["
   122  		}
   123  		if flag.Value.Type() == "string" {
   124  			// put quotes on the value
   125  			format = format + "=%q"
   126  		} else {
   127  			format = format + "=%s"
   128  		}
   129  		if len(flag.NoOptDefVal) > 0 {
   130  			format = format + "]"
   131  		}
   132  		format = format + "\t%s\n"
   133  		shorthand := flag.Shorthand
   134  		fmt.Fprintf(x, format, shorthand, flag.Name, flag.DefValue, flag.Usage)
   135  	})
   136  
   137  	return x.String()
   138  }
   139  
   140  func getSubCommands(cmd *cobra.Command) []*cobra.Command {
   141  	var subCommands []*cobra.Command
   142  	for _, subCmd := range cmd.Commands() {
   143  		subCommands = append(subCommands, subCmd)
   144  		subCommands = append(subCommands, getSubCommands(subCmd)...)
   145  	}
   146  	return subCommands
   147  }
   148  
   149  func usageFunc(cmd *cobra.Command) error {
   150  	subCommands := getSubCommands(cmd)
   151  	tabOut := getTabOutWithWriter(os.Stdout)
   152  	commandUsageTemplate.Execute(tabOut, struct {
   153  		Cmd         *cobra.Command
   154  		LocalFlags  string
   155  		GlobalFlags string
   156  		SubCommands []*cobra.Command
   157  		Version     string
   158  		APIVersion  string
   159  	}{
   160  		cmd,
   161  		etcdFlagUsages(cmd.LocalFlags()),
   162  		etcdFlagUsages(cmd.InheritedFlags()),
   163  		subCommands,
   164  		version.Version,
   165  		version.APIVersion,
   166  	})
   167  	tabOut.Flush()
   168  	return nil
   169  }
   170  
   171  func getTabOutWithWriter(writer io.Writer) *tabwriter.Writer {
   172  	aTabOut := new(tabwriter.Writer)
   173  	aTabOut.Init(writer, 0, 8, 1, '\t', 0)
   174  	return aTabOut
   175  }