github.com/loafoe/cli@v7.1.0+incompatible/actor/sharedaction/help.go (about)

     1  package sharedaction
     2  
     3  import (
     4  	"reflect"
     5  	"sort"
     6  	"strings"
     7  
     8  	"code.cloudfoundry.org/cli/actor/actionerror"
     9  	"code.cloudfoundry.org/cli/util/sorting"
    10  )
    11  
    12  const (
    13  	CommonCommandsIndent string = "  "
    14  	AllCommandsIndent    string = "   "
    15  	CommandIndent        string = "   "
    16  )
    17  
    18  // CommandInfo contains the help details of a command
    19  type CommandInfo struct {
    20  	// Name is the command name
    21  	Name string
    22  
    23  	// Description is the command description
    24  	Description string
    25  
    26  	// Alias is the command alias
    27  	Alias string
    28  
    29  	// Usage is the command usage string
    30  	Usage string
    31  
    32  	// Examples is the command examples string
    33  	Examples string
    34  
    35  	// RelatedCommands is a list of commands related to the command
    36  	RelatedCommands []string
    37  
    38  	// Flags contains the list of flags for this command
    39  	Flags []CommandFlag
    40  
    41  	// Environment is a list of environment variables specific for this command
    42  	Environment []EnvironmentVariable
    43  }
    44  
    45  // CommandFlag contains the help details of a command's flag
    46  type CommandFlag struct {
    47  	// Short is the short form of the flag
    48  	Short string
    49  
    50  	// Long is the long form of the flag
    51  	Long string
    52  
    53  	// Description is the description of the flag
    54  	Description string
    55  
    56  	// Default is the flag's default value
    57  	Default string
    58  }
    59  
    60  // EnvironmentVariable contains env vars specific for this command
    61  type EnvironmentVariable struct {
    62  	Name         string
    63  	Description  string
    64  	DefaultValue string
    65  }
    66  
    67  // HasUsage is an interface that commands may implement if they want to define their usage
    68  // text in a Usage() method, which gives them more flexibility than a struct tag.
    69  type HasUsage interface {
    70  	Usage() string
    71  }
    72  
    73  // HasExamples is an interface that commands may implement if they want to define their examples
    74  // text in a Examples() method, which gives them more flexibility than a struct tag.
    75  type HasExamples interface {
    76  	Examples() string
    77  }
    78  
    79  // CommandInfoByName returns the help information for a particular commandName in
    80  // the commandList.
    81  func (Actor) CommandInfoByName(commandList interface{}, commandName string) (CommandInfo, error) {
    82  	field, found := reflect.TypeOf(commandList).FieldByNameFunc(
    83  		func(fieldName string) bool {
    84  			field, _ := reflect.TypeOf(commandList).FieldByName(fieldName)
    85  			return field.Tag.Get("command") == commandName || field.Tag.Get("alias") == commandName
    86  		},
    87  	)
    88  
    89  	if !found {
    90  		return CommandInfo{}, actionerror.InvalidCommandError{CommandName: commandName}
    91  	}
    92  
    93  	tag := field.Tag
    94  	cmd := CommandInfo{
    95  		Name:        tag.Get("command"),
    96  		Description: tag.Get("description"),
    97  		Alias:       tag.Get("alias"),
    98  		Flags:       []CommandFlag{},
    99  		Environment: []EnvironmentVariable{},
   100  	}
   101  
   102  	fieldValue := reflect.ValueOf(commandList).FieldByIndex(field.Index)
   103  
   104  	if commandWithUsage, hasUsage := fieldValue.Interface().(HasUsage); hasUsage {
   105  		cmd.Usage = strings.ReplaceAll(
   106  			strings.TrimSpace(commandWithUsage.Usage()),
   107  			"\n",
   108  			"\n"+CommandIndent,
   109  		)
   110  	}
   111  
   112  	if commandWithExamples, hasExamples := fieldValue.Interface().(HasExamples); hasExamples {
   113  		cmd.Examples = strings.ReplaceAll(
   114  			strings.TrimSpace(commandWithExamples.Examples()),
   115  			"\n",
   116  			"\n"+CommandIndent,
   117  		)
   118  	}
   119  
   120  	command := field.Type
   121  	for i := 0; i < command.NumField(); i++ {
   122  		fieldTag := command.Field(i).Tag
   123  
   124  		if fieldTag.Get("hidden") != "" {
   125  			continue
   126  		}
   127  
   128  		if cmd.Usage == "" && fieldTag.Get("usage") != "" {
   129  			cmd.Usage = fieldTag.Get("usage")
   130  			continue
   131  		}
   132  
   133  		if cmd.Examples == "" && fieldTag.Get("examples") != "" {
   134  			cmd.Examples = fieldTag.Get("examples")
   135  			continue
   136  		}
   137  
   138  		if fieldTag.Get("related_commands") != "" {
   139  			relatedCommands := strings.Split(fieldTag.Get("related_commands"), ", ")
   140  			sort.Slice(relatedCommands, sorting.SortAlphabeticFunc(relatedCommands))
   141  			cmd.RelatedCommands = relatedCommands
   142  			continue
   143  		}
   144  
   145  		if fieldTag.Get("description") != "" {
   146  			cmd.Flags = append(cmd.Flags, CommandFlag{
   147  				Short:       fieldTag.Get("short"),
   148  				Long:        fieldTag.Get("long"),
   149  				Description: fieldTag.Get("description"),
   150  				Default:     fieldTag.Get("default"),
   151  			})
   152  		}
   153  
   154  		if fieldTag.Get("environmentName") != "" {
   155  			cmd.Environment = append(cmd.Environment, EnvironmentVariable{
   156  				Name:         fieldTag.Get("environmentName"),
   157  				DefaultValue: fieldTag.Get("environmentDefault"),
   158  				Description:  fieldTag.Get("environmentDescription"),
   159  			})
   160  		}
   161  	}
   162  
   163  	return cmd, nil
   164  }
   165  
   166  // CommandInfos returns a slice of CommandInfo that only fills in
   167  // the Name and Description for all the commands in commandList
   168  func (Actor) CommandInfos(commandList interface{}) map[string]CommandInfo {
   169  	handler := reflect.TypeOf(commandList)
   170  
   171  	infos := make(map[string]CommandInfo, handler.NumField())
   172  	for i := 0; i < handler.NumField(); i++ {
   173  		fieldTag := handler.Field(i).Tag
   174  		commandName := fieldTag.Get("command")
   175  		infos[commandName] = CommandInfo{
   176  			Name:        commandName,
   177  			Description: fieldTag.Get("description"),
   178  			Alias:       fieldTag.Get("alias"),
   179  		}
   180  	}
   181  
   182  	return infos
   183  }