github.com/henvic/wedeploycli@v1.7.6-0.20200319005353-3630f582f284/command/internal/template/template.go (about)

     1  package template
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"reflect"
     7  	"runtime"
     8  	"strings"
     9  
    10  	"github.com/henvic/wedeploycli/color"
    11  	"github.com/henvic/wedeploycli/formatter"
    12  	"github.com/spf13/cobra"
    13  	"github.com/spf13/pflag"
    14  )
    15  
    16  // Configure template for cobra commands
    17  func Configure(rootCmd *cobra.Command) {
    18  	cobra.AddTemplateFunc("printCommandsAndFlags", printCommandsAndFlags)
    19  	rootCmd.SetUsageTemplate(`{{printCommandsAndFlags .}}`)
    20  	rootCmd.SetHelpTemplate(`{{if not (eq .CommandPath "` + rootCmd.Name() + `")}}{{with or .Long .Short }}{{color FgYellow BgHiYellow "!"}} {{. | trim | color FgHiYellow}}
    21  {{end}}{{end}}{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}`)
    22  }
    23  
    24  type usagePrinter struct {
    25  	cmd                 *cobra.Command
    26  	cs                  []*cobra.Command
    27  	f                   *pflag.FlagSet
    28  	buf                 *bytes.Buffer
    29  	tw                  *formatter.TabWriter
    30  	flags               flagsDescriptions
    31  	showFlagsParamField bool
    32  
    33  	longHelp bool
    34  }
    35  
    36  type flagsDescriptions []flagDescription
    37  
    38  type flagDescription struct {
    39  	Name        string
    40  	Description []byte
    41  	Used        bool
    42  }
    43  
    44  func colorSpacingOffset() string {
    45  	if formatter.Human {
    46  		return "     "
    47  	}
    48  
    49  	return ""
    50  }
    51  
    52  func printCommandsAndFlags(cmd *cobra.Command) string {
    53  	up := &usagePrinter{
    54  		cmd: cmd,
    55  		cs:  cmd.Commands(),
    56  		f:   cmd.Flags(),
    57  	}
    58  
    59  	switch longHelp, err := up.f.GetBool("long-help"); {
    60  	case err != nil:
    61  		panic(err)
    62  	case longHelp:
    63  		up.longHelp = true
    64  	}
    65  
    66  	up.buf = new(bytes.Buffer)
    67  	up.tw = formatter.NewTabWriter(up.buf)
    68  	up.printAll()
    69  	return up.buf.String()
    70  }
    71  
    72  func (up *usagePrinter) printAll() {
    73  	var cmdPart = " [command]"
    74  
    75  	if len(up.cs) == 0 {
    76  		cmdPart = ""
    77  	}
    78  
    79  	useLine := strings.TrimSuffix(up.cmd.UseLine(), " [flags]")
    80  
    81  	usage := fmt.Sprintf("\n  Usage: %s%s",
    82  		useLine,
    83  		cmdPart)
    84  
    85  	if !up.cmd.DisableFlagsInUseLine && up.cmd.HasAvailableFlags() && !strings.Contains(usage, "[flag]") {
    86  		usage += " [flag]"
    87  	}
    88  
    89  	if up.cmd.Args == nil || runtime.FuncForPC(reflect.ValueOf(up.cmd.Args).Pointer()).Name() !=
    90  		runtime.FuncForPC(reflect.ValueOf(cobra.NoArgs).Pointer()).Name() {
    91  		usage += " [<args>]"
    92  	}
    93  
    94  	_, _ = fmt.Fprintf(up.buf, "%s\n\n", usage)
    95  
    96  	up.printCommands()
    97  	up.printFlags()
    98  	_ = up.tw.Flush()
    99  	up.printExamples()
   100  }
   101  
   102  func (up *usagePrinter) printExamples() {
   103  	if up.cmd.Example == "" {
   104  		return
   105  	}
   106  
   107  	_, _ = fmt.Fprintf(up.buf,
   108  		"\n%s%s\n\n",
   109  		color.Format(color.FgHiBlack, "  Examples\n"),
   110  		up.cmd.Example,
   111  	)
   112  }
   113  
   114  func (up *usagePrinter) printCommands() {
   115  	if len(up.cs) == 0 {
   116  		return
   117  	}
   118  
   119  	_, _ = fmt.Fprint(up.tw, color.Format(color.FgHiBlack, "  Command\t"+colorSpacingOffset()+"Description")+"\n")
   120  
   121  	for _, c := range up.cs {
   122  		if up.longHelp || c.IsAvailableCommand() {
   123  			_, _ = fmt.Fprintf(up.tw, "  %v\t%v\n", c.Name(), c.Short)
   124  		}
   125  	}
   126  
   127  	_, _ = fmt.Fprintln(up.tw, "\t") // \t here keeps the alignment between commands and flags
   128  }
   129  
   130  func (up *usagePrinter) printFlags() {
   131  	up.f.VisitAll(func(flag *pflag.Flag) {
   132  		if (up.longHelp || !flag.Hidden) && flag.Value.Type() != "bool" {
   133  			up.showFlagsParamField = true
   134  		}
   135  	})
   136  
   137  	if up.showFlagsParamField {
   138  		_, _ = fmt.Fprintf(up.tw, "%s\n",
   139  			color.Format(color.FgHiBlack,
   140  				"  Flag\t"+colorSpacingOffset()+"Parameter\t"+colorSpacingOffset()+"Description"))
   141  	} else {
   142  		_, _ = fmt.Fprintf(up.tw, "%s\n",
   143  			color.Format(color.FgHiBlack,
   144  				"  Flag\t"+colorSpacingOffset()+"Description"))
   145  	}
   146  
   147  	up.f.VisitAll(up.preparePrintFlag)
   148  
   149  	var begin = up.useFlagsHelpDescriptionFiltered([]string{
   150  		"service",
   151  		"environment",
   152  		"project",
   153  		"remote",
   154  		"url",
   155  	})
   156  
   157  	var end = up.useFlagsHelpDescriptionFiltered([]string{
   158  		"help",
   159  		"long-help",
   160  		"quiet",
   161  		"no-color",
   162  		"no-tty",
   163  		"verbose",
   164  		"defer-verbose",
   165  		"no-verbose-requests",
   166  	})
   167  
   168  	var middle = up.useFlagsHelpDescription()
   169  	_, _ = fmt.Fprintf(up.tw, "%s%s%s", string(begin), string(middle), string(end))
   170  }
   171  
   172  func (up *usagePrinter) useFlagsHelpDescriptionFiltered(list []string) []byte {
   173  	var buf bytes.Buffer
   174  
   175  	for _, filtered := range list {
   176  		for i, flag := range up.flags {
   177  			if flag.Name == filtered && !flag.Used {
   178  				up.flags[i].Used = true
   179  				buf.Write(flag.Description)
   180  			}
   181  		}
   182  	}
   183  
   184  	return buf.Bytes()
   185  }
   186  
   187  func (up *usagePrinter) useFlagsHelpDescription() []byte {
   188  	var buf bytes.Buffer
   189  
   190  	for i, flag := range up.flags {
   191  		if !flag.Used {
   192  			up.flags[i].Used = true
   193  			buf.Write(flag.Description)
   194  		}
   195  	}
   196  
   197  	return buf.Bytes()
   198  }
   199  
   200  func (up *usagePrinter) preparePrintFlag(flag *pflag.Flag) {
   201  	if flag.Deprecated != "" || (!up.longHelp && flag.Hidden) {
   202  		return
   203  	}
   204  
   205  	var buf = bytes.NewBufferString("  ")
   206  
   207  	if flag.Shorthand != "" && flag.ShorthandDeprecated == "" {
   208  		buf.WriteString(fmt.Sprintf("-%s, ", flag.Shorthand))
   209  	} else {
   210  		buf.WriteString("    ")
   211  	}
   212  
   213  	buf.WriteString(fmt.Sprintf("--%s", flag.Name))
   214  
   215  	if flag.NoOptDefVal != "" {
   216  		switch flag.Value.Type() {
   217  		case "string":
   218  			buf.WriteString(fmt.Sprintf("[=\"%s\"]", flag.NoOptDefVal))
   219  		case "bool":
   220  			if flag.NoOptDefVal != "true" {
   221  				buf.WriteString(fmt.Sprintf("[=%s]", flag.NoOptDefVal))
   222  			}
   223  		default:
   224  			buf.WriteString(fmt.Sprintf("[=%s]", flag.NoOptDefVal))
   225  		}
   226  	}
   227  
   228  	var flagType = flag.Value.Type()
   229  
   230  	if flagType == "bool" {
   231  		flagType = ""
   232  	}
   233  
   234  	if up.showFlagsParamField {
   235  		buf.WriteString(fmt.Sprintf("\t%s\t%s", flagType, flag.Usage))
   236  	} else {
   237  		buf.WriteString(fmt.Sprintf("\t%s", flag.Usage))
   238  	}
   239  
   240  	if !isDefaultFlagValueZero(flag) {
   241  		if flag.Value.Type() == "string" {
   242  			buf.WriteString(fmt.Sprintf(" (default %q)", flag.DefValue))
   243  		} else {
   244  			buf.WriteString(fmt.Sprintf(" (default %s)", flag.DefValue))
   245  		}
   246  	}
   247  
   248  	buf.WriteString("\n")
   249  	up.flags = append(up.flags, flagDescription{
   250  		Name:        flag.Name,
   251  		Description: buf.Bytes(),
   252  	})
   253  }
   254  
   255  func isDefaultFlagValueZero(f *pflag.Flag) bool {
   256  	switch f.DefValue {
   257  	case "", "0", "0s", "false", "[]":
   258  		return true
   259  	}
   260  
   261  	return false
   262  }