go.etcd.io/etcd@v3.3.27+incompatible/etcdctl/ctlv3/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 ctlv3
    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  	"github.com/spf13/cobra"
    30  	"github.com/spf13/pflag"
    31  )
    32  
    33  var (
    34  	commandUsageTemplate *template.Template
    35  	templFuncs           = template.FuncMap{
    36  		"descToLines": func(s string) []string {
    37  			// trim leading/trailing whitespace and split into slice of lines
    38  			return strings.Split(strings.Trim(s, "\n\t "), "\n")
    39  		},
    40  		"cmdName": func(cmd *cobra.Command, startCmd *cobra.Command) string {
    41  			parts := []string{cmd.Name()}
    42  			for cmd.HasParent() && cmd.Parent().Name() != startCmd.Name() {
    43  				cmd = cmd.Parent()
    44  				parts = append([]string{cmd.Name()}, parts...)
    45  			}
    46  			return strings.Join(parts, " ")
    47  		},
    48  	}
    49  )
    50  
    51  func init() {
    52  	commandUsage := `
    53  {{ $cmd := .Cmd }}\
    54  {{ $cmdname := cmdName .Cmd .Cmd.Root }}\
    55  NAME:
    56  {{ if not .Cmd.HasParent }}\
    57  {{printf "\t%s - %s" .Cmd.Name .Cmd.Short}}
    58  {{else}}\
    59  {{printf "\t%s - %s" $cmdname .Cmd.Short}}
    60  {{end}}\
    61  
    62  USAGE:
    63  {{printf "\t%s" .Cmd.UseLine}}
    64  {{ if not .Cmd.HasParent }}\
    65  
    66  VERSION:
    67  {{printf "\t%s" .Version}}
    68  {{end}}\
    69  {{if .Cmd.HasSubCommands}}\
    70  
    71  API VERSION:
    72  {{printf "\t%s" .APIVersion}}
    73  {{end}}\
    74  {{if .Cmd.HasSubCommands}}\
    75  
    76  
    77  COMMANDS:
    78  {{range .SubCommands}}\
    79  {{ $cmdname := cmdName . $cmd }}\
    80  {{ if .Runnable }}\
    81  {{printf "\t%s\t%s" $cmdname .Short}}
    82  {{end}}\
    83  {{end}}\
    84  {{end}}\
    85  {{ if .Cmd.Long }}\
    86  
    87  DESCRIPTION:
    88  {{range $line := descToLines .Cmd.Long}}{{printf "\t%s" $line}}
    89  {{end}}\
    90  {{end}}\
    91  {{if .Cmd.HasLocalFlags}}\
    92  
    93  OPTIONS:
    94  {{.LocalFlags}}\
    95  {{end}}\
    96  {{if .Cmd.HasInheritedFlags}}\
    97  
    98  GLOBAL OPTIONS:
    99  {{.GlobalFlags}}\
   100  {{end}}
   101  `[1:]
   102  
   103  	commandUsageTemplate = template.Must(template.New("command_usage").Funcs(templFuncs).Parse(strings.Replace(commandUsage, "\\\n", "", -1)))
   104  }
   105  
   106  func etcdFlagUsages(flagSet *pflag.FlagSet) string {
   107  	x := new(bytes.Buffer)
   108  
   109  	flagSet.VisitAll(func(flag *pflag.Flag) {
   110  		if len(flag.Deprecated) > 0 {
   111  			return
   112  		}
   113  		var format string
   114  		if len(flag.Shorthand) > 0 {
   115  			format = "  -%s, --%s"
   116  		} else {
   117  			format = "   %s   --%s"
   118  		}
   119  		if len(flag.NoOptDefVal) > 0 {
   120  			format = format + "["
   121  		}
   122  		if flag.Value.Type() == "string" {
   123  			// put quotes on the value
   124  			format = format + "=%q"
   125  		} else {
   126  			format = format + "=%s"
   127  		}
   128  		if len(flag.NoOptDefVal) > 0 {
   129  			format = format + "]"
   130  		}
   131  		format = format + "\t%s\n"
   132  		shorthand := flag.Shorthand
   133  		fmt.Fprintf(x, format, shorthand, flag.Name, flag.DefValue, flag.Usage)
   134  	})
   135  
   136  	return x.String()
   137  }
   138  
   139  func getSubCommands(cmd *cobra.Command) []*cobra.Command {
   140  	var subCommands []*cobra.Command
   141  	for _, subCmd := range cmd.Commands() {
   142  		subCommands = append(subCommands, subCmd)
   143  		subCommands = append(subCommands, getSubCommands(subCmd)...)
   144  	}
   145  	return subCommands
   146  }
   147  
   148  func usageFunc(cmd *cobra.Command) error {
   149  	subCommands := getSubCommands(cmd)
   150  	tabOut := getTabOutWithWriter(os.Stdout)
   151  	commandUsageTemplate.Execute(tabOut, struct {
   152  		Cmd         *cobra.Command
   153  		LocalFlags  string
   154  		GlobalFlags string
   155  		SubCommands []*cobra.Command
   156  		Version     string
   157  		APIVersion  string
   158  	}{
   159  		cmd,
   160  		etcdFlagUsages(cmd.LocalFlags()),
   161  		etcdFlagUsages(cmd.InheritedFlags()),
   162  		subCommands,
   163  		version.Version,
   164  		version.APIVersion,
   165  	})
   166  	tabOut.Flush()
   167  	return nil
   168  }
   169  
   170  func getTabOutWithWriter(writer io.Writer) *tabwriter.Writer {
   171  	aTabOut := new(tabwriter.Writer)
   172  	aTabOut.Init(writer, 0, 8, 1, '\t', 0)
   173  	return aTabOut
   174  }